วิธีแก้ไขข้อยกเว้น Stubbing ที่ไม่จำเป็น


107

รหัสของฉันมีดังต่อไปนี้

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

      }
}

ฉันได้รับข้อยกเว้นต่ำกว่า

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)

โปรดช่วยฉันวิธีแก้ไข

คำตอบ:


118

แทนที่@RunWith(MockitoJUnitRunner.class)ด้วย@RunWith(MockitoJUnitRunner.Silent.class).


45
ยินดีต้อนรับ. จะเป็นการดีที่จะอัปเดตคำตอบของคุณเพื่ออธิบายว่าเหตุใด OP จึงควรแทนที่รหัสดังกล่าว สิ่งนี้จะช่วยให้พวกเขาและผู้มาเยือนในอนาคตเข้าใจ
Bugs

5
Btw มันเป็น@RunWith(MockitoJUnitRunner.Silent.class)และไม่ SILENT
fgysin คืนสถานะโมนิกา

6
ใน Kotlin:@RunWith(MockitoJUnitRunner.Silent::class)
Juan Saravia

12
ไม่แน่ใจว่าทำไมคำตอบนี้ถึงได้รับการโหวตเพิ่มขึ้นโดยไม่มีคำอธิบาย คำตอบอื่นมีความหมายและถูกต้องกว่า
Yogesh

10
วิธีนี้ไม่ได้ช่วยแก้ปัญหา แต่เพียงแค่ระงับข้อความแสดงข้อผิดพลาดและจะส่งผลต่อการทดสอบอื่น ๆ ทั้งหมด (ถ้ามี) ในชั้นเรียน
Fencer

113

ในตอนแรกคุณควรตรวจสอบตรรกะการทดสอบของคุณ โดยปกติจะมี 3 กรณี ขั้นแรกคุณกำลังล้อเลียนวิธีการที่ไม่ถูกต้อง (คุณพิมพ์ผิดหรือมีคนเปลี่ยนรหัสทดสอบเพื่อไม่ให้ใช้วิธีการจำลองอีกต่อไป) ประการที่สองการทดสอบของคุณล้มเหลวก่อนที่จะเรียกวิธีนี้ ประการที่สามตรรกะของคุณผิดพลาดหาก / เปลี่ยนสาขาที่ใดที่หนึ่งในโค้ดเพื่อไม่ให้มีการเรียกวิธีการเยาะเย้ย

หากเป็นกรณีแรกคุณต้องการเปลี่ยนวิธีจำลองสำหรับวิธีที่ใช้ในรหัสเสมอ ขึ้นอยู่กับที่สองและสาม โดยปกติคุณควรลบการเยาะเย้ยนี้หากไม่มีประโยชน์ แต่บางครั้งก็มีบางกรณีในการทดสอบแบบพารามีทริกซึ่งควรใช้เส้นทางที่แตกต่างกันนี้หรือล้มเหลวก่อนหน้านี้ จากนั้นคุณสามารถแบ่งการทดสอบนี้ออกเป็นสองรายการหรือมากกว่านั้นแยกกัน แต่ก็ไม่ได้ดูดีเสมอไป วิธีการทดสอบ 3 วิธีที่มีผู้ให้บริการอาร์กิวเมนต์ 3 ตัวสามารถทำให้คุณทดสอบดูไม่สามารถอ่านได้ ในกรณีนั้นสำหรับ JUnit 4 คุณจะปิดข้อยกเว้นนี้ด้วย

@RunWith(MockitoJUnitRunner.Silent.class) 

คำอธิบายประกอบหรือหากคุณกำลังใช้แนวทางกฎ

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

หรือ (พฤติกรรมเดียวกัน)

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

สำหรับการทดสอบ JUnit 5 คุณสามารถปิดเสียงข้อยกเว้นนี้โดยใช้คำอธิบายประกอบที่ให้มาในmockito-junit-jupiterแพ็คเกจ

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

3
@MockitoSettings (ความเข้มงวด = ความเข้มงวด LENIENT) เป็นวิธีที่ง่ายที่สุดในการปรับความเข้มงวดในการตั้งค่าของฉัน ขอบคุณ!
Matt

7
คำตอบนี้ให้ภาพรวมที่ดีของความเป็นไปได้ อย่างไรก็ตามคุณยังสามารถตั้งค่าความเข้มงวดแบบผ่อนปรนเป็นกรณี ๆ ไปโดยใช้Mockito.lenient().when(...); สำหรับคำถามนี้โดยเฉพาะMockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);
neXus

กำหนด ExtendWith ใน superclass และ MockitoSettings ในคลาสย่อยเมื่อจัดการกับลำดับชั้นการทดสอบ หวังว่านี่จะช่วยประหยัดเวลาสำหรับใครบางคนเกี่ยวกับค่าใช้จ่ายของฉัน
miracle_the_V

40

การเงียบไม่ใช่วิธีแก้ปัญหา คุณต้องแก้ไขการเยาะเย้ยในการทดสอบของคุณ ดูเอกสารอย่างเป็นทางการที่นี่

ต้นขั้วที่ไม่จำเป็นคือการเรียกเมธอดที่ไม่ได้รับรู้ในระหว่างการดำเนินการทดสอบ (ดู MockitoHint ด้วย) เช่น:

//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
 ...

โปรดสังเกตว่าไม่เคยมีวิธีการที่ถูกตรึงไว้ในโค้ดภายใต้การทดสอบระหว่างการดำเนินการทดสอบ การลอกแบบหลงทางอาจเป็นการควบคุมดูแลของผู้พัฒนาสิ่งประดิษฐ์ของการคัดลอกวางหรือผลกระทบที่ไม่เข้าใจการทดสอบ / โค้ด ไม่ว่าจะด้วยวิธีใดก็ตามผู้พัฒนาพบกับโค้ดทดสอบที่ไม่จำเป็น เพื่อให้โค้ดเบสสะอาดและบำรุงรักษาได้จำเป็นต้องลบโค้ดที่ไม่จำเป็นออก การทดสอบอย่างอื่นจะอ่านและหาเหตุผลได้ยากกว่า

หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับการตรวจหาต้นขั้วที่ไม่ได้ใช้โปรดดู MockitoHint


16
มีหลายสถานการณ์ที่คุณเขียนการทดสอบ 8-9 รายการเทียบกับการตั้งค่า @BeforeEach ที่คล้ายกันซึ่งไม่ได้ใช้รายการที่ส่งคืนจากต้นขั้วเดียวเนื่องจากตรรกะทางธุรกิจในการทดสอบจำนวนหนึ่ง คุณสามารถ (A) แยกออกเป็นการทดสอบหลายรายการและคัดลอก / วางส่วน \ @BeforeEach อย่างมีประสิทธิภาพลบหนึ่งรายการ (B) คัดลอก / วางบรรทัดเดียวที่ Mockito เป็นอีโมเกี่ยวกับการทดสอบ 6 รายการที่ใช้และมี ไม่อยู่ใน 2 ที่ไม่ทำหรือ (C) ใช้ความเงียบ ฉันชอบใช้เงียบ / เตือน ไม่ใช่การทดสอบที่เสีย
RockMeetHardplace

1
@RockMeetHardplace Silent ไม่ใช่วิธีแก้ปัญหาคุณจะเห็นการคัดลอก / วางน้อยลงอย่างรวดเร็ว แต่เมื่อรักษาการทดสอบของคุณโดยคนใหม่ในโครงการของคุณสิ่งนี้จะเป็นปัญหา หากร้านหนังสือ Mockito ทำอย่างนั้นก็ไม่ใช่เพื่ออะไร
Stéphane GRILLON

2
@sgrillon: แต่ระบบนี้ตรวจจับเท็จบวกจำนวนมาก นั่นคือมันบอกว่ามีบางอย่างไม่ได้ใช้ แต่ก็ไม่ชัดเจนเนื่องจากการลบต้นขั้วจะทำให้การดำเนินการแตก มันไม่ได้ว่ารหัสการทดสอบไม่สามารถปรับปรุงได้ก็ว่าเป็นเส้นที่สำคัญของ stubbing ควรไม่เคยถูกตรวจพบว่า "ไม่จำเป็น" ดังนั้นความสำคัญของความสามารถในการปิดใช้งานการตรวจสอบนี้จึงกระตือรือร้นเกินไป
Carighan

1
@Carighan หากการเยาะเย้ยของคุณถูกตรวจพบว่าไม่ถูกต้องอาจไม่ใช่อย่างที่คุณคิด สิ่งนี้ช่วยให้คุณได้รับการทดสอบที่ดีในขณะที่อาจมีข้อบกพร่อง
Stéphane GRILLON

@sgrillon ขอโทษที่ฉันไม่ได้กลับไปหาคุณในเรื่องนั้น ปรากฎว่าเคยมีข้อบกพร่องเกิดขึ้นซึ่งขึ้นอยู่กับลำดับการดำเนินการทดสอบซึ่งจะสร้าง "การเข้าชมที่ผิดพลาด" โดยที่ต้นขั้วที่ใช้ในการทดสอบหนึ่ง แต่เขียนทับในอีกรายการหนึ่งจะเรียกใช้ แม้ว่าจะแก้ไขมานานเท่าที่ฉันสามารถบอกได้
Carighan

34

สำหรับฉันทั้งข้อเสนอแนะ@Ruleหรือ@RunWith(MockitoJUnitRunner.Silent.class)ข้อเสนอแนะไม่ได้ผล เป็นโครงการเดิมที่เราอัปเกรดเป็น mockito-core 2.23.0

เราสามารถกำจัดUnnecessaryStubbingExceptionโดยใช้:

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

แทน:

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

ไม่จำเป็นต้องบอกว่าคุณควรดูรหัสทดสอบ แต่เราจำเป็นต้องรวบรวมสิ่งต่างๆและการทดสอบทำงานก่อนอื่น;)


8
IMHO นี่เป็นคำตอบที่มีประโยชน์ที่สุดที่นี่ฉันพบแทนที่จะปิดเสียงคลาสทดสอบทั้งหมด
priyeshdkr

1
เนื่องจากฉันต้องการระงับการล้อเลียนเพียง 1 ครั้งนี่คือคำตอบที่ดีที่สุดสำหรับฉัน ไม่ใช่คำตอบสำหรับ OP จริงๆ
Hans Wouters

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

whenที่นี่กำหนดค่าจำลองของคุณทำอะไร อย่างไรก็ตามคุณไม่ได้ใช้การเยาะเย้ยนี้ในทางใด ๆ อีกต่อไปหลังจากบรรทัดนี้ (นอกเหนือจากการทำ a verify) Mockito เตือนคุณว่าwhenเส้นนั้นไม่มีจุดหมาย บางทีคุณอาจทำผิดพลาดทางตรรกะ?


ขอบคุณสำหรับความช่วยเหลือ
VHS

ฉันต้องการทั้งเวลาและตรวจสอบคำสั่งกรุณาแนะนำวิธีดำเนินการต่อไป
VHS

2
เรียกใช้ฟังก์ชันในคลาสทดสอบของคุณ ( Service) เพื่อดูว่าตอบสนองอย่างถูกต้องหรือไม่ คุณไม่ได้ทำอย่างนั้นเลยคุณจะทดสอบอะไรที่นี่?
john16384

3

ดูส่วนหนึ่งของการติดตามสแต็กดูเหมือนว่าคุณกำลังขีดฆ่าที่dao.doSearch()อื่น เช่นเดียวกับการสร้างต้นขั้วของวิธีการเดิมซ้ำ ๆ

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.

ลองพิจารณาคลาสทดสอบด้านล่างตัวอย่างเช่น:

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

ฉันอยากจะพิจารณาปรับโครงสร้างการทดสอบของคุณให้แตกในกรณีที่จำเป็น


2

ในกรณีของฉันข้อผิดพลาด Mockito กำลังบอกให้ฉันเรียกใช้วิธีการจริงหลังจากwhenหรือwheneverต้นขั้ว เนื่องจากเราไม่ได้เรียกใช้เงื่อนไขที่เราเพิ่งล้อเลียน Mockito จึงรายงานว่าเป็นส่วนที่ไม่จำเป็นหรือรหัส

นี่คือสิ่งที่เป็นเหมือนเมื่อข้อผิดพลาดเกิดขึ้น:

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

จากนั้นฉันก็เรียกวิธีการจริงที่กล่าวถึงเมื่อคำสั่งเพื่อล้อเลียนวิธีการ

การเปลี่ยนแปลงที่ทำมีดังต่อไปนี้ 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)
}

มันใช้งานได้แล้ว


1

หากคุณใช้รูปแบบนี้แทน:

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

แทนที่ด้วย:

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

1

ฉันเคยUnnecessaryStubbingExceptionพยายามใช้whenวิธีการบนวัตถุ Spy Mockito.lenient()ปิดเสียงข้อยกเว้น แต่ผลการทดสอบไม่ถูกต้อง

ในกรณีของวัตถุ Spy เราต้องเรียกใช้วิธีการโดยตรง

@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

แทนที่

@RunWith(MockitoJUnitRunner.class)

ด้วย

@RunWith(MockitoJUnitRunner.Silent.class)

หรือลบ@RunWith(MockitoJUnitRunner.class)

หรือเพียงแค่แสดงความคิดเห็นเกี่ยวกับการโทรล้อเลียนที่ไม่ต้องการ


0

ในกรณีที่เป็นโครงการขนาดใหญ่การแก้ไขข้อยกเว้นเหล่านี้แต่ละข้อทำได้ยาก ในเวลาเดียวกันSilentไม่แนะนำให้ใช้ ฉันได้เขียนสคริปต์เพื่อลบส่วนที่ไม่จำเป็นทั้งหมดตามรายการ

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

เราเพียงแค่ต้องคัดลอก - วางmvnผลลัพธ์และเขียนรายการข้อยกเว้นเหล่านี้โดยใช้ regex และปล่อยให้สคริปต์ดูแลส่วนที่เหลือ


-1

เมื่อคุณสร้างการเยาะเย้ยและไม่ได้ใช้การจำลองนั้นจะทำให้เกิดข้อยกเว้นที่ไม่ได้ใช้ ในกรณีของคุณที่ล้อเลียนไม่ได้เรียกว่าจริง ดังนั้นจึงเป็นการโยนข้อผิดพลาดนั้น ดังนั้น relpace @RunWith(MockitoJUnitRunner.class)ด้วย @RunWith(MockitoJUnitRunner.Silent.class)ซึ่งจะลบข้อผิดพลาด หากคุณยังต้องการใช้@RunWith(MockitoJUnitRunner.class)ให้ลองแก้จุดบกพร่องตรรกะของคุณว่าฟังก์ชันที่คุณล้อเลียนนั้นถูกเรียกใช้จริงหรือไม่


สิ่งนี้ไม่ได้เพิ่มสิ่งที่ไม่ครอบคลุมในคำตอบที่มีอยู่
Matthew อ่าน

-3

หากคุณใช้ใด ๆ () เมื่อเยาะเย้ยคุณต้อง relpace กับ@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.Silent.class)


นี่เป็นเพียงเท็จ any()ทำงานได้ดีกับนักวิ่งทั่วไปเมื่อใช้อย่างถูกต้อง
Matthew อ่าน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.