Mockito를 사용하여 일반 매개 변수로 클래스를 조롱


280

일반 매개 변수로 클래스를 조롱하는 깨끗한 방법이 있습니까? 내가 Foo<T>기대하는 메소드에 전달 해야하는 클래스를 조롱해야한다고 가정 해보십시오 Foo<Bar>. 다음을 쉽게 수행 할 수 있습니다.

Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());

가정하면 getValue()제네릭 형식을 반환합니다 T. 그러나 나중에 기대하는 방법으로 새끼 고양이를 옮길 때 새끼 고양이가 생길 것 Foo<Bar>입니다. 캐스팅이 이것을하는 유일한 수단입니까?

답변:


280

나는 그것을 캐스팅해야한다고 생각하지만 너무 나쁘지 않아야합니다.

Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());

34
예, 그러나 여전히 경고가 있습니다. 경고를 피할 수 있습니까?
odwl

12
@SuppressWarnings ( "unchecked")
qualidafial

18
단위 테스트에서 모의 ​​객체에 대해 이야기하고 있기 때문에 이것이 완전히 수용 가능하다고 생각합니다.
Magnilex

1
@demaniak 전혀 작동하지 않습니다. 해당 컨텍스트에서는 인수 매처를 사용할 수 없습니다.
Krzysztof Krasoń

1
@demaniak 그것은 잘 컴파일되지만 테스트를 실행할 때 InvalidUseOfMatchersException (RuntimeException)
Superole

277

이 문제를 해결하는 또 다른 방법은 @Mock주석을 대신 사용하는 것입니다. 모든 경우에 작동하지는 않지만 훨씬 더 섹시 해 보입니다. :)

예를 들면 다음과 같습니다.

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

    @Test
    public void testFoo() {
        when(fooMock.getValue()).thenReturn(new Bar());
    }
}

MockitoJUnitRunner주석이 달린 필드를 초기화합니다 @Mock.


3
이것은 1.9.5에서 더 이상 사용되지 않습니다. :( 나에게 많은 청소기를 보인다.
코드 수련원에게

12
@ CodeNovitiate 1.9.5에서 MockitoJUnitRunner 및 Mock에 대한 지원 중단 주석을 찾을 수 없습니다. 더 이상 사용되지 않는 것은 무엇입니까? (예, org.mockito.MockitoAnnotations.Mock은 더 이상 사용되지 않지만 대신 org.mockito.Mock을 사용해야합니다)
neu242

12
잘 했어, 이것은 나를 위해 완벽하게 작동했다. "성적"일뿐 아니라를 사용하지 않고 경고를 피할 수 SuppressWarnings있습니다. 경고는 이유가 있기 때문에이를 억제하는 습관을 들이지 않는 것이 좋습니다. 감사!
Nicole

4
@Mock대신 에 사용 하는 것이 마음에 들지 않는 것이 있습니다 mock(). 시공 시간 동안 필드가 여전히 null이므로 해당 시간에 종속성을 삽입 할 수 없으며 필드를 최종적으로 만들 수 없습니다. 전자는 @Before물론 주석 이 달린 방법 으로 해결할 수 있습니다 .
Rüdiger Schulz

3
시작하려면 MockitoAnnotations.initMocks (this);
borjab

42

지정할 일반 유형을 만족하는 중간 클래스 / 인터페이스를 항상 작성할 수 있습니다. 예를 들어, Foo가 인터페이스 인 경우 테스트 클래스에서 다음 인터페이스를 작성할 수 있습니다.

private interface FooBar extends Foo<Bar>
{
}

Foo가 최종 클래스 가 아닌 상황에서는 다음 코드를 사용하여 클래스를 확장하고 동일한 작업을 수행 할 수 있습니다.

public class FooBar extends Foo<Bar>
{
}

그런 다음 위 코드 중 하나를 다음 코드와 함께 사용할 수 있습니다.

Foo<Bar> mockFoo = mock(FooBar.class);
when(mockFoo.getValue()).thenReturn(new Bar());

4
제공 Foo하는 인터페이스 또는 비 최종 클래스이며,이 상당히 우아한 해결책이 될 것으로 보인다. 감사.
Tim Clemons

최종 클래스가 아닌 클래스에 대한 예제도 포함하도록 답변을 업데이트했습니다. 인터페이스에 대해 코딩하는 것이 이상적이지만 항상 그런 것은 아닙니다. 잘 잡아!
dsingleton

16

테스트 유틸리티 메소드를 작성하십시오 . 두 번 이상 필요한 경우 특히 유용합니다.

@Test
public void testMyTest() {
    // ...
    Foo<Bar> mockFooBar = mockFoo();
    when(mockFooBar.getValue).thenReturn(new Bar());

    Foo<Baz> mockFooBaz = mockFoo();
    when(mockFooBaz.getValue).thenReturn(new Baz());

    Foo<Qux> mockFooQux = mockFoo();
    when(mockFooQux.getValue).thenReturn(new Qux());
    // ...
}

@SuppressWarnings("unchecked") // still needed :( but just once :)
private <T> Foo<T> mockFoo() {
    return mock(Foo.class);
}

당신이 조롱하려는 클래스를 전달하는 일반적인 유틸리티 메소드를 만들기 위해 답을 확장 할 수 있습니다.
William Dutton

1
@WilliamDutton static <T> T genericMock(Class<? super T> classToMock) { return (T)mock(classToMock); }: 단일 억제조차 필요하지 않습니다 :) 그러나 조심하고 Integer num = genericMock(Number.class)컴파일하지만 던집니다 ClassCastException. 가장 일반적인 G<P> mock = mock(G.class)경우 에만 유용합니다 .
TWiStErRob

6

클래스 또는 메서드에서 실수로 억제 된 다른 경고를 간과 할 수 있으므로 경고를 억제해서는 안된다는 데 동의합니다. 그러나 IMHO 한 줄의 코드에만 영향을주는 경고를 표시하지 않는 것이 합리적입니다.

@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);

3

흥미로운 경우가있다 : 메소드는 제네릭 콜렉션을 수신하고 동일한 기본 유형의 제네릭 콜렉션을 리턴합니다. 예를 들면 다음과 같습니다.

Collection<? extends Assertion> map(Collection<? extends Assertion> assertions);

이 메소드는 Mockito anyCollectionOf matcher와 Answer를 조합하여 조롱 할 수 있습니다.

when(mockedObject.map(anyCollectionOf(Assertion.class))).thenAnswer(
     new Answer<Collection<Assertion>>() {
         @Override
         public Collection<Assertion> answer(InvocationOnMock invocation) throws Throwable {
             return new ArrayList<Assertion>();
         }
     });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.