2016-10-12 3 views
2

문자열을 키로 사용하고 조롱 된 클래스 Candidate의 인스턴스를 값으로 사용하는 Map을 만들고 싶습니다.JMockit이 두 개 이상의 클래스 인스턴스를 모의 수 없습니다.

Map<String, Long> domainNameToId = new HashMap<String, Long>(); 
    domainNameToId.put("farmaciapuentezurita.es", 1234l); 
    domainNameToId.put("vivefarma.com", 2345l); 
    domainNameToId.put("eurofarmacia.com", 3456l); 

    Map<String, Candidate> expectedCandidates = new HashMap<String, Candidate>(); 
    for(String domain : domainNameToId.keySet()) { 
     final Candidate cand = new MockUp<Candidate>() { 
      @Mock Long getDomainId() { return domainNameToId.get(domain); } // private method 
      @Mock boolean validateAndPrepare() { return true; } 
      @Mock String getRepresentingName() { return domain; } 
     }.getMockInstance(); 
     expectedCandidates.put(domain, cand); 
    } 

1.28에 1.20에서 JMockit를 업그레이드하기 전에 작동하는 데 사용되는 위의 코드.

java.lang.IllegalStateException :

는 지금은 예외 얻을에 무 모형 에서 클래스 com.urlservice.data.Candidate의 초기화되지 않은 인스턴스를 얻기 위해 잘못된 시도를 ...

내가 설명서를 읽고 다음과 같은 방식으로 대신 new MockUp(T targetInstance)을 사용하려고 (이 루프의 몸) :

final Candidate cand = new Candidate(domain); 
new MockUp<Candidate>(cand) { 
    @Mock Long getDomainId() { return domainNameToId.get(domain); } // private method 
    @Mock boolean validateAndPrepare() { return true; } 
    @Mock String getRepresentingName() { return domain; } 
}; 

결과가 매우 이상합니다. 처음 후보자가 조롱을 당했고 나머지 조롱 된 후보자는 전혀 조롱을받지 않았고 실제 방법이 호출되었습니다.

java.lang.IllegalArgumentException가 : 이미 조롱 : 클래스 com.urlservice.data.Candidate 에서 아무 소용

final Candidate cand = new Candidate(domain); 
new Expectations(cand) {{ 
    cand.getDomainId(); result = domainNameToId.get(domain); // Had to make it public :-(
    cand.validateAndPrepare(); result = true; 
    cand.getRepresentingName(); result = domain; 
}}; 

:

나는 다시 Expectations API로 되돌리려 시도 ...

정말 최신 버전으로 업그레이드하고 싶지만이 문제의 해결 방법을 찾을 수 없습니다.

업데이트 : 최대 1.28 버전에서이 문제를 재현하지 못했습니다. 따라서이 버전이 출시 된 것 같습니다.

또한 제 2의 예 (new MockUp(T targetInstance))와 관련하여, 클래스 MockUp 라인 402의 소스 코드를 살펴보고 예상되는 동작이 첫 번째 인스턴스가 아닌 특정 타겟 인스턴스를 조롱하지 않는 것처럼 보입니다.

MockUp<?> previousMockUp = findPreviouslyFakedClassIfMockUpAlreadyApplied(); 

    if (previousMockUp != null) { 
    targetType = previousMockUp.targetType; 
    mockedClass = previousMockUp.mockedClass; 
    return; // Input param targetInstance is disregarded 
    } 

무엇이 누락 되었습니까?

UPDATE2 : 나는 실패한 테스트 예를 생각해 냈습니다. 조금 번거 롭긴하지만 포인트를 얻을 것이라고 확신합니다.

public class SampleTest { 

class TestedClass { 
    private IncrementingDependency dep; 
    TestedClass(IncrementingDependency dep) { this.dep = dep; } 
    public int getVal() { return dep.inc(); } 
} 

class IncrementingDependency { 
    int val; 
    public IncrementingDependency(int val) { this.val = val; } 
    public int inc() { return ++val; } 
} 

@Test 
public void sampleTest() { 
    List<Integer> inputVals = Arrays.asList(1, 2, 3); 
    List<TestedClass> incrementingClasses = new ArrayList<TestedClass>(); 

    for (Integer num : inputVals) { 
     IncrementingDependency dep = new IncrementingDependency(num); 
     new MockUp<IncrementingDependency>(dep) { 
      @Mock int inc() { return num; } // Mock with different behavior - DON'T INCREMENT 
     }; 
     incrementingClasses.add(new TestedClass(dep)); 
    } 

    assertThat(incrementingClasses.get(0).getVal()).isEqualTo(1); // Passes - 1 wasn't incremented (mocked behavior) 
    assertThat(incrementingClasses.get(1).getVal()).isEqualTo(2); // Fails - real code was called and 2 was incremented to 3 
    assertThat(incrementingClasses.get(2).getVal()).isEqualTo(3); // We never get to this point 
} 
} 

이 예제는 실패하지 않았을 경우에도, 내가 MockUp의 생성자에 전달하기 전에 내 의존성을 인스턴스화 할 필요가 있다는 사실은 기껏해야 문제가 있다는 점 유의 하시길 바랍니다. 모의 객체를 만드는 전체적인 요점은 인스턴스화 할 필요가 없다는 것입니다.
private void expectCandidatesFromMap(final Map<String, Long> domainNameToId) { 
    Map<String, Candidate> expectedCandidates = new HashMap<String, Candidate>(); 

    class MockedCandidate extends MockUp<Candidate> { 
     private final String domainName; 
     private final Long domainId; 

     MockedCandidate(String domainName) { 
      this.domainName = domainName; 
      this.domainId = domainNameToId.get(domainName); 
     } 

     @Mock Long getDomainId() { return domainId; } 
     @Mock String getRepresentingName() { return domainName; } 
     @Mock boolean validateAndPrepare() { return true; } 
    } 

    for (String domain : domainNameToId.keySet()) { 
     expectedCandidates.put(domain, new MockedCandidate(domain).getMockInstance()); 
    } 
} 

내 테스트는 지금은 '돈에 합격하더라도 :

+0

'새로운 MockUp (cand)'을 사용하면 "이상한"결과를 재현 할 수 없습니다. 그것은 1.28로 잘 작동하는 것 같습니다. 실패한 예제 테스트를 보여줄 수 있습니까? –

+0

'Mock (T)'에 대한 [API 문서] (http://jmockit.org/api1x/mockit/MockUp.html#MockUp-T-)에는 "주어진 인스턴스에만 영향을 미친다"고되어 있습니다. 따라서,'Candidate'의 * 다른 * 인스턴스에서 호출 된 메소드는'@ Mock' 메소드로 넘어 가지 않습니다. –

+0

@ Rogério 물론, 그게 내가 성취하려고했던 것입니다. 나는 실패한 예제 테스트를 최대한 빨리 제공하려고 노력할 것입니다. 그러나 JMockit 코드 샘플을 맨 끝에 넣으면 내 주장이 강력하게 뒷받침되지 않습니까? – KidCrippler

답변

0

밀접하게 (매우 명확하고 조직이다) JMockit의 실제 프로젝트 단위 테스트를 조사 후, 나는 다소 독특한 방식으로 문제를 해결하기 위해 관리 왜 클래스의 특정 인스턴스를 모방하는 유일한 방법은 다른 ad-hoc 클래스를 작성하는 것인가 (이 버전 이전에 익숙했던 것처럼 익명으로 인라이닝하는 것과 반대되는) 이유를 완전히 이해합니다.

이 접근 방식은 더 많은 행 (개인 필드 선언, ctor 구현)을 필요로하지만 논증 할 수있을 정도로 우아합니다.

JMockit의 공헌자 중 하나가이 문제에 대해 밝히면 매우 만족 스러울 것입니다.

+0

사실, 이것이 유일한 방법은 아닙니다 ('Invocation' 클래스 참조). 그리고 위와 같이 이름 붙여진 모형을 만들 필요는 없습니다. 익명의 하위 클래스로 작성 될 수있는 "상태 저장"모형 (인스턴스 필드 포함) 만 있으면됩니다. 실제 문제는 동일한 모형 클래스를 여러 관련 인스턴스에 여러 번 적용하는 것입니다. 이것은 JMockit이 조롱 된 인스턴스에서 모형 인스턴스로 내부 맵을 유지해야하기 때문에 지원되지 않습니다. 기술적으로 가능하지만 API의 의도 된 용도에 맞지 않습니다. –

+0

기본적으로 내가 여기서 한 것은 명명 된 모형으로 API의 의도 된 사용을 우회하는 것입니다. 의도 한 용도가 아닌 이유를 자세히 설명해 주시겠습니까? – KidCrippler

+0

실물 크기 모형은 복잡하거나 값 비싼 행동이 거의 또는 전혀없는 "데이터"개체를 만드는 편리한 방법이 아닌 * 위조 * 구현을 제공하기위한 것입니다 (대부분 클래스). 대신 테스트는 필요한만큼의 인스턴스에서 원하는 상태로 클래스를 인스턴스화해야합니다. 상기 인스턴스의 조롱 또는 가짜는 없습니다. –

관련 문제