Sử dụng Mockito để mô phỏng các lớp với các tham số chung


280

Có một phương pháp sạch để chế nhạo một lớp với các tham số chung không? Nói rằng tôi phải chế nhạo một lớp Foo<T>mà tôi cần truyền vào một phương thức mong đợi mộtFoo<Bar> . Tôi có thể làm như sau đủ dễ dàng:

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

Giả sử getValue()trả về loại chung T. Nhưng điều đó sẽ có mèo con khi sau này tôi chuyển nó thành một phương pháp mong đợi Foo<Bar>. Là đúc phương tiện duy nhất để làm điều này?

Câu trả lời:


280

Tôi nghĩ rằng bạn cần phải bỏ nó, nhưng nó không quá tệ:

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

34
Có nhưng bạn vẫn có một cảnh báo. Có thể tránh cảnh báo?
odwl

12
@SuppressWarnings ("không được kiểm tra")
Qualidafial

18
Tôi nghĩ rằng điều này là hoàn toàn chấp nhận được vì chúng ta đang nói về một đối tượng giả trong một bài kiểm tra đơn vị.
Magnilex

1
@demaniak Nó không hoạt động. Các đối số đối số không thể được sử dụng trong bối cảnh đó.
Krzysztof Krasnoyń

1
@demaniak Điều đó sẽ biên dịch tốt, nhưng khi chạy thử nghiệm, nó sẽ ném UnlimitedUseOfMatchersException (là một RuntimeException)
Superole

277

Một cách khác để sử dụng là sử dụng @Mock chú thích thay thế. Không hoạt động trong mọi trường hợp, nhưng trông quyến rũ hơn nhiều :)

Đây là một ví dụ:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

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

Việc MockitoJUnitRunnerkhởi tạo các trường được chú thích bằng @Mock.


3
điều này không được chấp nhận trong 1.9.5. :( Có vẻ sạch sẽ hơn đối với tôi.
Code Novitiate

12
@CodeNovitiate Tôi không thể tìm thấy bất kỳ chú thích phản đối nào trên MockitoJUnitRunner và Mock trong 1.9.5. Vì vậy, những gì được phản đối? (Có, org.mockito.MockitoAnnotations.Mock không được dùng nữa, nhưng bạn nên sử dụng org.mockito.Mock thay thế)
neu242

12
Làm tốt lắm, điều này làm việc hoàn hảo cho tôi. Nó không chỉ "quyến rũ" hơn, mà còn tránh một cảnh báo mà không sử dụng SuppressWarnings. Cảnh báo tồn tại vì một lý do, tốt hơn hết là đừng có thói quen đàn áp chúng. Cảm ơn!
Nicole

4
Có một điều tôi không thích khi sử dụng @Mockthay vì mock(): các trường vẫn không có trong thời gian xây dựng, vì vậy tôi không thể chèn phụ thuộc vào thời điểm đó và không thể làm cho các trường cuối cùng. Điều trước đây có thể được giải quyết bằng một @Beforephương pháp -annotated tất nhiên.
Rüdiger Schulz

3
Để bắt đầu, chỉ cần gọi MockitoAnnotations.initMocks (cái này);
borjab

42

Bạn luôn có thể tạo một lớp / giao diện trung gian thỏa mãn loại chung mà bạn muốn chỉ định. Ví dụ: nếu Foo là một giao diện, bạn có thể tạo giao diện sau trong lớp thử nghiệm của mình.

private interface FooBar extends Foo<Bar>
{
}

Trong trường hợp Foo là một lớp không phải là cuối cùng , bạn có thể mở rộng lớp với mã sau đây và làm điều tương tự:

public class FooBar extends Foo<Bar>
{
}

Sau đó, bạn có thể sử dụng một trong các ví dụ trên với mã sau:

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

4
Được cung cấp Foolà một giao diện hoặc lớp không phải là cuối cùng, đây dường như là một giải pháp hợp lý thanh lịch. Cảm ơn.
Tim Clemons

Tôi đã cập nhật câu trả lời để bao gồm các ví dụ cho các lớp không phải là cuối cùng. Lý tưởng nhất là bạn sẽ mã hóa trên một giao diện, nhưng điều đó không phải lúc nào cũng đúng. Nắm bắt tốt!
DSingleton

16

Tạo một phương thức thử nghiệm tiện ích . Đặc biệt hữu ích nếu bạn cần nó nhiều lần.

@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);
}

Có thể mở rộng câu trả lời của bạn để thực hiện một phương thức tiện ích chung đi qua lớp bạn muốn chế giễu.
William Dutton

1
@WilliamDutton static <T> T genericMock(Class<? super T> classToMock) { return (T)mock(classToMock); }thậm chí không cần một lần triệt tiêu nào :) Nhưng hãy cẩn thận, Integer num = genericMock(Number.class)biên dịch, nhưng ném ClassCastException. Điều này chỉ hữu ích cho G<P> mock = mock(G.class)trường hợp phổ biến nhất .
TWiStErRob

6

Tôi đồng ý rằng người ta không nên ngăn chặn các cảnh báo trong các lớp hoặc phương thức vì người ta có thể bỏ qua các cảnh báo khác, vô tình bị triệt tiêu. Nhưng IMHO hoàn toàn hợp lý để ngăn chặn một cảnh báo chỉ ảnh hưởng đến một dòng mã.

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

3

Đây là một trường hợp thú vị: phương thức nhận bộ sưu tập chung và trả về bộ sưu tập chung có cùng loại cơ sở. Ví dụ:

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

Phương pháp này có thể được mô phỏng bằng sự kết hợp của trình so khớp Mockito anyCollectionOf và Câu trả lời.

when(mockedObject.map(anyCollectionOf(Assertion.class))).thenAnswer(
     new Answer<Collection<Assertion>>() {
         @Override
         public Collection<Assertion> answer(InvocationOnMock invocation) throws Throwable {
             return new ArrayList<Assertion>();
         }
     });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.