如何解决不必要的存根异常

2020/11/30 23:22 · java ·  · 0评论

我的代码如下

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

请帮我解决

替换@RunWith(MockitoJUnitRunner.class)@RunWith(MockitoJUnitRunner.Silent.class)

首先,您应该检查测试逻辑。通常有3种情况。首先,您在模拟错误的方法(您输入了错误或有人更改了测试代码,因此不再使用模拟方法)。其次,在调用此方法之前,测试失败。第三,如果/ switch在代码中的某处分支,则逻辑将出错,因此不会调用模拟方法。

如果是第一种情况,您总是想更改代码中使用的模拟方法。第二和第三取决于它。通常,如果该模拟没有用,则应删除该模拟。但是,有时在参数化测试中会出现某些情况,这些情况应该采用这种不同的方法,或者更早失效。然后,您可以将此测试分为两个或多个单独的测试,但这并不总是好看的。具有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 {
}

沉默不是解决方案。您需要在测试中修复模拟。请参阅此处的官方文档

不必要的存根是在测试执行期间从未实现的存根方法调用(另请参见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。

对我来说@Rule@RunWith(MockitoJUnitRunner.Silent.class)建议建议都不起作用。这是一个旧项目,我们升级到了模仿核心2.23.0。

我们可以UnnecessaryStubbingException使用以下方法摆脱掉

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

代替:

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

不用说,您应该看一下测试代码,但是我们需要先编译好东西,然后首先运行测试;)

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

when这里配置您的模拟做一些事情。但是,在此行之后,您将不再使用该模拟程序(除了执行之外verify)。Mockito警告您,这when条线毫无意义。也许您犯了逻辑错误?

查看堆栈跟踪的一部分,就好像您在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();
    }
}

我宁愿考虑在必要时将测试重构为存根。

好吧,在我的情况下,Mockito错误告诉我在whenwhenever存根之后调用实际方法由于我们没有调用刚刚嘲笑的条件,因此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)
}

然后我只是调用了when语句中提到的实际方法来模拟该方法。

所做的更改如下
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)
}

现在正在工作。

更换

@RunWith(MockitoJUnitRunner.class)

@RunWith(MockitoJUnitRunner.Silent.class)

删除@RunWith(MockitoJUnitRunner.class)

只是注释掉不需要的模拟呼叫(显示为未经授权的存根)。

如果您改用这种风格:

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

替换为:

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

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

对于大型项目,很难解决所有这些异常。同时,使用Silent不建议使用。我已经编写了一个脚本,以删除所有不必要的存根,并给出了它们的列表。

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

我们只需要复制粘贴mvn输出并使用regex编写这些异常的列表,然后让脚本来处理其余的事情。

这已经在指出此评论,但我认为这是太容易忽视:你可能会遇到的UnnecessaryStubbingException,如果你简单地替换现有转换的JUnit 4测试类JUnit的5测试类@Before@BeforeEach,如果你执行一些磕碰在至少一种测试用例无法实现的设置方法中

这个Mockito线程具有更多信息,基本上,@Before之间的测试执行存在细微的差异@BeforeEach使用@Before,只要有一个测试用例就可以实现存根就足够了,使用@BeforeEach所有用例都必须如此。

如果您不想将其设置@BeforeEach分解为很多小部分(如上面正确引用的注释所指出的那样),那么还有另一种方法可以代替为整个测试类激活宽松模式:您可以仅在其中添加这些存根@BeforeEach单独使用宽大方法lenient()

当您创建一个模拟并且不使用该模拟时,它将引发未使用的存根异常。在您的情况下,该模拟实际上并未被调用。因此,它将引发该错误。因此,relpace@RunWith(MockitoJUnitRunner.class)@RunWith(MockitoJUnitRunner.Silent.class)这将消除错误。如果您仍然想使用,请@RunWith(MockitoJUnitRunner.class)尝试调试您的逻辑(如果您模拟的函数实际上被调用或未被调用)。

如果在模拟时使用any(),则必须@RunWith(MockitoJUnitRunner.class)使用
@RunWith(MockitoJUnitRunner.Silent.class)

本文地址:http://java.askforanswer.com/ruhejiejuebubiyaodecungenyichang.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!