Using Mockito to mock classes with generic parameters

后端 未结 7 1853
不知归路
不知归路 2020-11-30 19:37

Is there a clean method of mocking a class with generic parameters? Say I have to mock a class Foo which I need to pass into a method that expects a <

相关标签:
7条回答
  • 2020-11-30 19:42

    I think you do need to cast it, but it shouldn't be too bad:

    Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
    when(mockFoo.getValue()).thenReturn(new Bar());
    
    0 讨论(0)
  • 2020-11-30 19:45

    Here is an interesting case: method receieves generic collection and returns generic collection of same base type. For example:

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

    This method can be mocked with combination of Mockito anyCollectionOf matcher and the 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>();
             }
         });
    
    0 讨论(0)
  • 2020-11-30 19:50

    Create a test utility method. Specially useful if you need it for more than once.

    @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);
    }
    
    0 讨论(0)
  • 2020-11-30 19:51

    I agree that one shouldn't suppress warnings in classes or methods as one could overlook other, accidentally suppressed warnings. But IMHO it's absolutely reasonable to suppress a warning that affects only a single line of code.

    @SuppressWarnings("unchecked")
    Foo<Bar> mockFoo = mock(Foo.class);
    
    0 讨论(0)
  • 2020-11-30 19:55

    One other way around this is to use @Mock annotation instead. Doesn't work in all cases, but looks much sexier :)

    Here's an example:

    @RunWith(MockitoJUnitRunner.class)
    public class FooTests {
    
        @Mock
        public Foo<Bar> fooMock;
    
        @Test
        public void testFoo() {
            when(fooMock.getValue()).thenReturn(new Bar());
        }
    }
    

    The MockitoJUnitRunner initializes the fields annotated with @Mock.

    0 讨论(0)
  • 2020-11-30 20:02

    You could always create an intermediate class/interface that would satisfy the generic type that you are wanting to specify. For example, if Foo was an interface, you could create the following interface in your test class.

    private interface FooBar extends Foo<Bar>
    {
    }
    

    In situations where Foo is a non-final class, you could just extend the class with the following code and do the same thing:

    public class FooBar extends Foo<Bar>
    {
    }
    

    Then you could consume either of the above examples with the following code:

    Foo<Bar> mockFoo = mock(FooBar.class);
    when(mockFoo.getValue()).thenReturn(new Bar());
    
    0 讨论(0)
提交回复
热议问题