Mockito: Các phương thức khai thác có kiểu trả về với các thẻ hoang dã bị ràng buộc


135

Xem xét mã này:

public class DummyClass {
    public List<? extends Number> dummyMethod() {
        return new ArrayList<Integer>();
    }
}
public class DummyClassTest {
    public void testMockitoWithGenerics() {
        DummyClass dummyClass = Mockito.mock(DummyClass.class);
        List<? extends Number> someList = new ArrayList<Integer>();
        Mockito.when(dummyClass.dummyMethod()).thenReturn(someList); //Compiler complains about this
    }
}

Trình biên dịch phàn nàn về dòng đang cố gắng xử lý hành vi dummyMethod(). Bất kỳ con trỏ nào về cách người ta đi về các phương pháp khai thác trả về một loại với các thẻ hoang dã bị ràng buộc?


Bạn có thể cập nhật đoạn mã của mình để hiển thị các loại chung không?
máy

1
Làm xong. Tôi đã phải xóa các thẻ trước và mã, chúng bị tước bỏ <? mở rộng Số> từ khai báo kiểu.
Shikhar Mishra

Câu trả lời:


190

Bạn cũng có thể sử dụng phương thức an toàn không loại doReturn cho mục đích này,

@Test
public void testMockitoWithGenerics()
{
    DummyClass dummyClass = Mockito.mock(DummyClass.class);
    List<? extends Number> someList = new ArrayList<Integer>();

    Mockito.doReturn(someList).when(dummyClass).dummyMethod();

    Assert.assertEquals(someList, dummyClass.dummyMethod());
}

như đã thảo luận trên nhóm google của Mockito.

Trong khi điều này đơn giản hơn thenAnswer, một lần nữa lưu ý rằng nó không phải là loại an toàn. Nếu bạn lo lắng về an toàn loại, câu trả lời của millhouse là chính xác.

Chi tiết bổ sung

Để rõ ràng, đây là lỗi trình biên dịch quan sát,

The method thenReturn(List<capture#1-of ? extends Number>) in the type OngoingStubbing<List<capture#1-of ? extends Number>> is not applicable for the arguments (List<capture#2-of ? extends Number>)

Tôi tin rằng trình biên dịch đã gán loại ký tự đại diện đầu tiên trong suốt whencuộc gọi và sau đó không thể xác nhận rằng loại ký tự đại diện thứ hai trong thenReturncuộc gọi là giống nhau.

Có vẻ như thenAnswerkhông gặp phải vấn đề này vì nó chấp nhận loại ký tự đại diện trong khi thenReturncó loại không phải ký tự đại diện, phải được ghi lại. Từ OngockStubbing của Mockito ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);

điều này cũng giúp tôi một phần ... nhưng điều gì xảy ra nếu danh sách bạn mong đợi trở lại không trống?
ttati

thay vì có một danh sách trống, bạn cũng có thể thực hiện: List <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati

32

Tôi giả sử bạn muốn có thể tải lên someListvới một số giá trị đã biết; Dưới đây là một cách tiếp cận sử dụng Answer<T>cùng với phương pháp trợ giúp templated để giữ mọi thứ an toàn:

@Test
public void testMockitoWithGenericsUsingAnswer()
{
    DummyClass dummyClass =  Mockito.mock(DummyClass.class);

    Answer<List<Integer>> answer = setupDummyListAnswer(77, 88, 99);
    Mockito.when(dummyClass.dummyMethod()).thenAnswer(answer);

    ...
}

private <N extends Number> Answer<List<N>> setupDummyListAnswer(N... values) {
    final List<N> someList = new ArrayList<N>();

    someList.addAll(Arrays.asList(values));

    Answer<List<N>> answer = new Answer<List<N>>() {
        public List<N> answer(InvocationOnMock invocation) throws Throwable {
            return someList;
        }   
    };
    return answer;
}

17

Tôi đã đánh điều tương tự ngày hôm qua. Cả hai câu trả lời từ @ nondescript1 và @millhouse đã giúp tôi tìm ra cách giải quyết. Tôi đã sử dụng khá nhiều mã giống như @millhouse, ngoại trừ việc tôi làm cho nó chung chung hơn một chút, vì lỗi của tôi không phải do a java.util.List, mà là com.google.common.base.Optional. Do đó, phương thức trợ giúp nhỏ của tôi cho phép mọi loại Tvà không chỉ List<T>:

public static <T> Answer<T> createAnswer(final T value) {
    Answer<T> dummy = new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return value;
        }
    };
    return dummy;
}

Với phương thức trợ giúp này bạn có thể viết:

Mockito.when(dummyClass.dummyMethod()).thenAnswer(createAnswer(someList));

Điều này biên dịch tốt và làm điều tương tự như thenReturn(...)phương pháp.

Có ai biết nếu lỗi mà trình biên dịch Java phát ra là lỗi trình biên dịch hay nếu mã thực sự không chính xác?


Điều này có vẻ đơn giản, đơn giản và, gần như tôi có thể nói, chính xác. Tôi không chắc tại sao Mockito không cung cấp thứ gì đó tương tự như vậy ....... trừ khi nó không?
vacao

14
Trong Java 8 có thể rút ngắn : Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList), vì vậy không cần phương thức tiện ích
fikovnik

1
@fikovnik Thật là một khám phá tuyệt vời "thenAnswer"!
borjab

5

Tôi đang biến nhận xét của fikovnik thành câu trả lời ở đây để cung cấp cho nó rõ hơn vì tôi nghĩ đó là giải pháp thanh lịch nhất khi sử dụng Java 8+.

Các tài liệu Mockito khuyến cáo sử dụng doReturn()(như đề xuất trong câu trả lời được chấp nhận) chỉ như là một phương sách cuối cùng.

Thay vào đó, để tránh lỗi trình biên dịch được mô tả trong câu hỏi, when()phương pháp Mockito được đề xuất có thể được sử dụng với thenAnswer()và lambda (thay vì phương thức trợ giúp):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)

mặc dù nó không đưa ra bất kỳ lỗi thời gian biên dịch nào, danh sách trả về trống ngay cả khi chúng ta chuyển danh sách có mục.
Venkatesh Kolla - người dùng2742897

0

Mặc dù phương pháp tiện ích được đề xuất bởi Marek Radonsky hoạt động, nhưng cũng có một tùy chọn khác thậm chí không yêu cầu biểu thức lambda (tìm kiếm lạ) mà fikovnik đề xuất:

câu trả lời này cho một câu hỏi tương tự cho thấy, bạn cũng có thể sử dụng như sau:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
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.