Cách giải quyết ngoại lệ Stubbing không cần thiết


100

Mã của tôi như bên dưới,

@RunWith(MockitoJUnitRunner.class)
public class MyClass {

    private static final String code ="Test";

    @Mock
     private MyClassDAO dao;

    @InjectMocks
     private MyClassService Service = new MyClassServiceImpl();

    @Test
     public void testDoSearch() throws Exception {
         final String METHOD_NAME = logger.getName().concat(".testDoSearchEcRcfInspections()");
         CriteriaDTO dto = new CriteriaDTO();
         dto.setCode(code);
         inspectionService.searchEcRcfInspections(dto);
         List<SearchCriteriaDTO> summaryList = new ArrayList<SearchCriteriaDTO>();
         inspectionsSummaryList.add(dto);
         when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
         verify(dao).doSearchInspections(dto);

      }
}

Tôi nhận được dưới đây ngoại lệ

org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected in test class: Test
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at service.Test.testDoSearch(Test.java:72)
Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.
  at org.mockito.internal.exceptions.Reporter.formatUnncessaryStubbingException(Reporter.java:838)
  at org.mockito.internal.junit.UnnecessaryStubbingsReporter.validateUnusedStubs(UnnecessaryStubbingsReporter.java:34)
  at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:49)
  at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:103)
  at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
  at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Xin hãy giúp tôi cách giải quyết

Câu trả lời:


120

Thay thế @RunWith(MockitoJUnitRunner.class)bằng @RunWith(MockitoJUnitRunner.Silent.class).


44
Chào mừng. sẽ rất đáng để cập nhật câu trả lời của bạn để giải thích tại sao họ OP nên thay thế mã như vậy. Điều này sẽ giúp họ và những du khách trong tương lai hiểu được.
Lỗi

5
Btw, nó là @RunWith(MockitoJUnitRunner.Silent.class)không SILENT
fgysin Khôi phục Monica

6
Ở Kotlin:@RunWith(MockitoJUnitRunner.Silent::class)
Juan Saravia,

9
Không chắc tại sao câu trả lời này vẫn nhận được sự ủng hộ mà không có lời giải thích. Các câu trả lời khác có ý nghĩa và chính xác hơn.
Yogesh

8
Điều này không giải quyết được vấn đề mà chỉ đơn giản là loại bỏ thông báo lỗi và cũng sẽ ảnh hưởng đến tất cả các bài kiểm tra khác (nếu có) trong lớp.
Fencer

97

Lúc đầu, bạn nên kiểm tra logic kiểm tra của mình. Thông thường có 3 trường hợp. Đầu tiên, bạn đang chế nhạo sai phương pháp (bạn mắc lỗi đánh máy hoặc ai đó đã thay đổi mã đã thử nghiệm để phương pháp chế nhạo đó không còn được sử dụng nữa). Thứ hai, bài kiểm tra của bạn không thành công trước khi phương pháp này được gọi. Thứ ba, logic của bạn rơi vào sai lầm nếu / chuyển nhánh ở đâu đó trong mã để phương thức bị chế nhạo đó không được gọi.

Nếu đây là trường hợp đầu tiên, bạn luôn muốn thay đổi phương pháp chế nhạo cho phương thức được sử dụng trong mã. Với thứ hai và thứ ba thì tùy. Thông thường, bạn chỉ nên xóa mô hình này nếu nó không có tác dụng. Nhưng đôi khi có một số trường hợp nhất định trong các bài kiểm tra tham số hóa, sẽ đi theo con đường khác hoặc thất bại sớm hơn. Sau đó, bạn có thể chia bài kiểm tra này thành hai hoặc nhiều bài kiểm tra riêng biệt nhưng không phải lúc nào cũng đẹp. 3 phương pháp kiểm tra có thể có 3 nhà cung cấp đối số có thể khiến bạn kiểm tra trông khó đọc. Trong trường hợp đó đối với JUnit 4, bạn im lặng ngoại lệ này với

@RunWith(MockitoJUnitRunner.Silent.class) 

chú thích hoặc nếu bạn đang sử dụng cách tiếp cận quy tắc

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT);

hoặc (cùng một hành vi)

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();

Đối với các bài kiểm tra JUnit 5, bạn có thể tắt ngoại lệ này bằng cách sử dụng chú thích được cung cấp trong mockito-junit-jupitergói.

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class JUnit5MockitoTest {
}

3
@MockitoSettings (nghiêm ngặt = Nghiêm ngặt.LENIENT) là cách đơn giản nhất để điều chỉnh độ nghiêm ngặt trong thiết lập của tôi. Cảm ơn!
Matt,

4
Câu trả lời này cung cấp một cái nhìn tổng quan tốt về các khả năng. Tuy nhiên, bạn cũng có thể thiết lập mức độ nghiêm khắc khoan hồng trên cơ sở từng trường hợp bằng cách sử dụng Mockito.lenient().when(...); cho câu hỏi này đặc biệt nó sẽ làMockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);
Nexus

Xác định ExtendWith trong lớp cha và MockitoSettings trong lớp con, khi xử lý các cấu trúc phân cấp thử nghiệm. Hy vọng điều này tiết kiệm thời gian cho ai đó về chi phí của tôi.
magic_the_V

33

Im lặng không phải là một giải pháp. Bạn cần sửa bài thi thử của mình. Xem tài liệu chính thức tại đây .

Phần sơ khai không cần thiết là các lời gọi phương thức sơ khai không bao giờ được nhận ra trong quá trình thực thi thử nghiệm (xem thêm MockitoHint), ví dụ:

//code under test:
 ...
 String result = translator.translate("one")
 ...

 //test:
 ...
 when(translator.translate("one")).thenReturn("jeden"); // <- stubbing realized during code execution
 when(translator.translate("two")).thenReturn("dwa"); // <- stubbing never realized
 ...

Lưu ý rằng một trong những phương thức sơ khai không bao giờ được thực hiện trong mã đang được thử nghiệm, trong quá trình thực thi thử nghiệm. Sơ hở sai lệch có thể là sự giám sát của nhà phát triển, tạo tác của việc sao chép-dán hoặc hiệu ứng không hiểu bài kiểm tra / mã. Dù bằng cách nào, nhà phát triển sẽ nhận được mã kiểm tra không cần thiết. Để giữ cho cơ sở mã sạch và có thể bảo trì được, cần phải loại bỏ các mã không cần thiết. Nếu không, các bài kiểm tra khó đọc và khó suy luận hơn.

Để tìm hiểu thêm về cách phát hiện các gốc không sử dụng, hãy xem MockitoHint.


13
Có nhiều trường hợp bạn viết 8-9 bài kiểm tra dựa trên thiết lập @BeforeEach tương tự trong đó mục trả lại từ một bản gốc không được sử dụng do logic nghiệp vụ trong một số bài kiểm tra. Bạn có thể (A) chia nhỏ nó thành nhiều bài kiểm tra và sao chép / dán hiệu quả phần \ @BeforeEach trừ một mục (B) Sao chép / dán một dòng duy nhất mà Mockito yêu thích vào 6 bài kiểm tra sử dụng nó và có nó not in the 2 that not hoặc (C) Sử dụng im lặng. Tôi thích sử dụng im lặng / cảnh báo. Nó không phải là một bài kiểm tra hỏng.
RockMeetHardplace

1
@RockMeetHardplace, Im lặng không phải là một giải pháp , nhanh chóng bạn thấy ít sao chép / dán hơn nhưng khi duy trì các thử nghiệm của bạn bởi những người mới trong dự án của bạn, điều này sẽ có vấn đề. Nếu hiệu sách Mockito làm được điều đó thì không phải là không có.
Stéphane GRILLON

2
@sgrillon: Nhưng hệ thống này phát hiện vô số kết quả dương tính giả. Đó là, nó nói rằng một cái gì đó không được sử dụng, nhưng rõ ràng là không, vì việc loại bỏ phần sơ khai sẽ phá vỡ quá trình thực thi. Không phải là mã kiểm tra không thể được cải thiện, mà là một dòng sơ khai quan trọng không bao giờ được phát hiện là "không cần thiết". Do đó tầm quan trọng của việc có thể vô hiệu hóa kiểm tra này, nó quá háo hức.
Carighan

@Carighan, nếu mô hình của bạn bị phát hiện là không chính xác, có thể nó không như bạn nghĩ. Điều này cung cấp cho bạn một bài kiểm tra OK trong khi có thể có lỗi.
Stéphane GRILLON

@sgrillon, xin lỗi, tôi không bao giờ liên hệ lại với bạn về điều đó. Nó chỉ ra rằng đã từng có một lỗi với điều này trong đó tùy thuộc vào thứ tự thực hiện thử nghiệm, nó sẽ tạo ra "lần truy cập sai", trong đó các bản khai được sử dụng trong một thử nghiệm nhưng bị ghi đè trong một thử nghiệm khác sẽ kích hoạt nó. Tuy nhiên, nó đã được sửa từ lâu, theo như tôi có thể nói.
Carighan

27

Đối với tôi cả @Rulenhững @RunWith(MockitoJUnitRunner.Silent.class)gợi ý đều không hiệu quả. Đó là một dự án kế thừa mà chúng tôi đã nâng cấp lên mockito-core 2.23.0.

Chúng tôi có thể loại bỏ UnnecessaryStubbingExceptionbằng cách sử dụng:

Mockito.lenient().when(mockedService.getUserById(any())).thenReturn(new User());

thay vì:

when(mockedService.getUserById(any())).thenReturn(new User());

Không cần phải nói rằng bạn nên xem mã thử nghiệm, nhưng chúng tôi cần phải biên dịch nội dung và các thử nghiệm đang chạy trước hết;)


6
IMHO. Đây là câu trả lời hữu ích nhất ở đây mà tôi tìm thấy thay vì làm im lặng cả lớp kiểm tra.
priyeshdkr

Vì tôi chỉ muốn ngăn chặn 1 lời chế giễu, đây là câu trả lời tốt nhất cho tôi. Tuy nhiên, không thực sự là một câu trả lời cho OP.
Hans Wouters

25
 when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
 verify(dao).doSearchInspections(dto);

whenđây cấu hình mô hình của bạn để làm điều gì đó. Tuy nhiên, bạn không sử dụng mô hình này theo bất kỳ cách nào nữa sau dòng này (ngoài việc làm a verify). Mockito cảnh báo bạn rằng whendòng do đó là vô nghĩa. Có lẽ bạn đã mắc lỗi logic?


Cảm ơn sự giúp đỡ của bạn
VHS

Tôi cần cả tuyên bố khi nào và xác minh vui lòng đề xuất cách tiến xa hơn
VHS

2
Gọi một hàm trên lớp thử nghiệm của bạn ( Service) để xem nó có phản ứng đúng hay không. Bạn đã không làm điều đó ở tất cả, vậy bạn đang thử nghiệm cái gì ở đây?
john16384

3

Nhìn vào một phần của dấu vết ngăn xếp của bạn, có vẻ như bạn đang khai thác phần dao.doSearch()khác. Giống như lặp đi lặp lại việc tạo các sơ khai của cùng một phương thức.

Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at service.Test.testDoSearch(Test.java:72)
Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.

Hãy xem xét Lớp kiểm tra dưới đây chẳng hạn:

@RunWith(MockitoJUnitRunner.class)
public class SomeTest {
    @Mock
    Service1 svc1Mock1;

    @Mock
    Service2 svc2Mock2;

    @InjectMock
    TestClass class;

    //Assume you have many dependencies and you want to set up all the stubs 
    //in one place assuming that all your tests need these stubs.

    //I know that any initialization code for the test can/should be in a 
    //@Before method. Lets assume there is another method just to create 
    //your stubs.

    public void setUpRequiredStubs() {
        when(svc1Mock1.someMethod(any(), any())).thenReturn(something));
        when(svc2Mock2.someOtherMethod(any())).thenReturn(somethingElse);
    }

    @Test
    public void methodUnderTest_StateUnderTest_ExpectedBehavior() {
        // You forget that you defined the stub for svcMock1.someMethod or 
        //thought you could redefine it. Well you cannot. That's going to be 
        //a problem and would throw your UnnecessaryStubbingException.
       when(svc1Mock1.someMethod(any(),any())).thenReturn(anyThing);//ERROR!
       setUpRequiredStubs();
    }
}

Tôi muốn xem xét việc cấu trúc lại các bài kiểm tra của bạn để sơ khai khi cần thiết.


2

Nếu bạn đang sử dụng kiểu này để thay thế:

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);

thay thế bằng:

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();

2

Thay thế

@RunWith(MockitoJUnitRunner.class)

với

@RunWith(MockitoJUnitRunner.Silent.class)

hoặc loại bỏ@RunWith(MockitoJUnitRunner.class)

hoặc chỉ nhận xét về các cuộc gọi chế giễu không mong muốn (được hiển thị là khai báo trái phép).


1

Tôi đã có UnnecessaryStubbingExceptionkhi tôi cố gắng sử dụng các whenphương thức trên một đối tượng Spy. Mockito.lenient()im lặng ngoại lệ nhưng kết quả kiểm tra không chính xác.

Trong trường hợp đối tượng Spy, người ta phải gọi các phương thức trực tiếp.

@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
class ArithmTest {

    @Spy
    private Arithm arithm;

    @Test
    void testAddition() {

        int res = arithm.add(2, 5);

        // doReturn(7).when(arithm).add(2, 5);
        assertEquals(res, 7);
    }
}

1

Chà, trong trường hợp của tôi, lỗi Mockito đã yêu cầu tôi gọi phương thức thực sau dấu whenhoặc wheneversơ khai. Vì chúng tôi không viện dẫn các điều kiện mà chúng tôi vừa chế nhạo, Mockito đã báo cáo rằng đó là các đoạn mã hoặc đoạn mã không cần thiết.

Đây là những gì nó xảy ra khi lỗi sắp xảy ra:

@Test
fun `should return error when item list is empty for getStockAvailability`() {
    doAnswer(
        Answer<Void> { invocation ->
            val callback =
                invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
            callback.onApiCallError(stockResultViewStateError)
            null
        }
    ).whenever(stockViewModelTest)
        .getStockAvailability(listOf(), getStocksApiCallBack)
}

thì tôi chỉ gọi phương thức thực được đề cập trong câu lệnh when để chế nhạo phương thức.

những thay đổi được thực hiện như bên dưới stockViewModelTest.getStockAvailability(listOf(), getStocksApiCallBack)

@Test
fun `should return error when item list is empty for getStockAvailability`() {
    doAnswer(
        Answer<Void> { invocation ->
            val callback =
                invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
            callback.onApiCallError(stockResultViewStateError)
            null
        }
    ).whenever(stockViewModelTest)
        .getStockAvailability(listOf(), getStocksApiCallBack)
    //called the actual method here
    stockViewModelTest.getStockAvailability(listOf(), getStocksApiCallBack)
}

Nó đang làm việc bây giờ.


0

Trong trường hợp một dự án lớn, rất khó để sửa từng trường hợp ngoại lệ này. Đồng thời, việc sử dụng Silentkhông được khuyến khích. Tôi đã viết một kịch bản để loại bỏ tất cả những sơ khai không cần thiết với danh sách chúng.

https://gist.github.com/cueo/da1ca49e92679ac49f808c7ef594e75b

Chúng ta chỉ cần sao chép-dán mvnđầu ra và viết danh sách các ngoại lệ này bằng cách sử dụng regex và để tập lệnh lo phần còn lại.


0

Nếu bạn sử dụng bất kỳ () nào khi chế nhạo, bạn phải tạo lại @RunWith (MockitoJUnitRunner.class) bằng @RunWith (MockitoJUnitRunner.Silent.class).


0

Khi bạn tạo một mô hình và mô hình đó không được sử dụng, nó sẽ ném ra một ngoại lệ không được sử dụng. Trong trường hợp của bạn, mô hình đó không thực sự được gọi. Do đó nó đang ném lỗi đó. Do đó, relpace @RunWith(MockitoJUnitRunner.class)với @RunWith(MockitoJUnitRunner.Silent.class)đó sẽ loại bỏ các lỗi. Nếu bạn vẫn muốn sử dụng, @RunWith(MockitoJUnitRunner.class)hãy thử gỡ lỗi logic của bạn xem chức năng bạn đã chế tạo có thực sự được gọi hay không.

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.