如何解决不需要的Stubbing异常

VHS*_*VHS 79 java spring mockito

我的代码如下,

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

      }
}
Run Code Online (Sandbox Code Playgroud)

我正在低于例外

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)
Run Code Online (Sandbox Code Playgroud)

请帮我解决一下

小智 101

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

  • 欢迎.非常值得更新你的答案来解释_why_他们OP应该替换这样的代码.这将有助于他们和未来的访问者了解. (30认同)
  • 顺便说一下,它是`@RunWith(MockitoJUnitRunner.Silent.class)`和*不*SILENT (5认同)
  • 在Kotlin:`@RunWith(MockitoJUnitRunner.Silent :: class)` (5认同)
  • 请忽略此答案并删除不需要的存根 (5认同)
  • 投了反对票,因为这不是解决方案。您只是隐藏了问题,即您正在嘲笑某些东西,因此可能期望它被调用,但没有被使用。 (5认同)
  • 不知道为什么这个答案会不断增加而不加解释。其他答案更加有意义和准确。 (3认同)
  • 这不能解决问题,而只是消除错误消息,并且还会影响该类中的所有其他测试(如果有)。 (2认同)

Dco*_*tez 60

首先,您应该检查您的测试逻辑.通常有3个案例.首先,你是在嘲笑错误的方法(你做了一个拼写错误或有人更改了经过测试的代码,因此不再使用模拟方法).其次,在调用此方法之前,您的测试失败.第三,你的逻辑在代码中的某个地方出现了错误的if/switch分支,因此不会调用mocked方法.

如果这是第一种情况,您总是希望更改代码中使用的方法的模拟方法.它取决于第二和第三.通常你应该删除这个模拟,如果它没用.但有时在参数化测试中存在某些情况,这些情况应该采用这种不同的路径或更早地失败.然后你可以将这个测试分成两个或多个单独的测试,但这并不总是很好看.可能有3个参数提供程序的3个测试方法可以使您的测试看起来不可读.在JUnit 4的情况下,您可以使用其中任何一个来声明此异常

@RunWith(MockitoJUnitRunner.Silent.class) 
Run Code Online (Sandbox Code Playgroud)

注释或者您正在使用规则方法

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT);
Run Code Online (Sandbox Code Playgroud)

或(相同的行为)

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();
Run Code Online (Sandbox Code Playgroud)

对于JUnit 5测试,您可以使用mockito-junit-jupiter包中提供的注释来声明此异常.

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class JUnit5MockitoTest {
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案很好地概述了可能性。但是,您也可以使用 `Mockito.lenient().when(...)` 根据具体情况设置宽松的严格性;对于这个特定问题,它将是“Mockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);” (14认同)
  • @MockitoSettings(strictness = Strictness.LENIENT) 是在我的设置中调整严格性的最简单方法。谢谢! (3认同)
  • 从 2.23.3 开始,你可以通过注释`@Mock(lenient = true)`设置宽松的模拟 (3认同)
  • 如果能提供进口就好了。对于“严格性”,我看到了 2 个软件包的建议。`org.mockito` 和 `org.mockito.quality` (2认同)

joh*_*384 22

 when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
 verify(dao).doSearchInspections(dto);
Run Code Online (Sandbox Code Playgroud)

when这里配置您的模拟做一些事情.但是,你不能在这一行之后以任何方式使用这个模拟(除了做一个verify).Mockito警告你,when因此这条线是毫无意义的.也许你犯了一个逻辑错误?

  • 在测试类(`Service`)上调用一个函数,看它是否正确反应.你根本没有这样做,所以你在这里测试什么? (2认同)

sgr*_*lon 21

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

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

注意,在测试执行期间,被测试的代码之一从未在被测试的代码中实现。流氓存根可能是开发人员的疏忽,复制粘贴的人工产物或不了解测试/代码的影响。无论哪种方式,开发人员最终都会得到不必要的测试代码。为了保持代码库的清洁和可维护性,有必要删除不必要的代码。否则,测试将更难以阅读和推理。

要了解有关检测未使用的存根的更多信息,请参见MockitoHint。

  • 在许多情况下,您针对类似的@BeforeEach设置编写8-9个测试,其中由于少数测试的业务逻辑,一个存根返回的项目未使用。您可以(A)将其分解为多个测试,然后有效地复制/粘贴\ @BeforeEach部分减去一个项目(B)复制/粘贴Mockito即将执行的单行代码到使用它的6个测试中并将其粘贴不在2个不这样做的范围内或(C)使用无声。我更喜欢使用静音/警告。这不是一个失败的测试。 (7认同)
  • @sgrillon:但是这个系统检测到大量的误报。也就是说,它说某些东西未使用,但显然不是,因为删除存根会中断执行。并不是说测试代码不能改进,而是一条重要的存根行*永远*不应被检测为“不必要的”。因此能够禁用此检查的重要性,它太急切了。 (6认同)
  • @sgrillon,抱歉我没有回复你。事实证明,这曾经存在一个错误,根据测试执行顺序,它会生成“错误命中”,其中在一个测试中使用但在另一个测试中被覆盖的存根会触发它。据我所知,它已经修复很久了。 (2认同)

phi*_*ous 13

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

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

Mockito.lenient().when(mockedService.getUserById(any())).thenReturn(new User());
Run Code Online (Sandbox Code Playgroud)

代替:

when(mockedService.getUserById(any())).thenReturn(new User());
Run Code Online (Sandbox Code Playgroud)

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

  • 恕我直言。这是我在这里找到的最有用的答案,而不是让整个测试类保持沉默。 (10认同)
  • 因为我只想抑制 1 个嘲笑,所以这对我来说是最好的答案。但这并不是OP的真正答案。 (2认同)

qbe*_*ben 9

正如其他人指出的那样,删除不必要地存根方法调用的行通常是最简单的。

就我而言,它位于 a 中@BeforeEach并且大多数时候都是相关的。在唯一未使用该方法的测试中,我重置了模拟,例如:

myMock.reset()
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助其他遇到同样问题的人。

(请注意,如果同一个模拟上有多个模拟调用,这也可能会很不方便,因为您必须模拟除未调用的方法之外的所有其他方法。)

  • 我喜欢这种方法。但有一件事,我认为该方法是“reset(myMock)”,而不是“myMock.reset()” (3认同)

Hei*_*löd 8

这已经在评论中指出了,但我认为这太容易被忽视了:如果您UnnecessaryStubbingException只是通过将现有的替换为 JUnit 4 测试类来将 JUnit 4 测试类转换为 JUnit 5 测试类@Before@BeforeEach并且如果您执行一些存根,您可能会遇到在该设置方法中,至少有一个测试用例未实现。

这个 Mockito 线程@Before有更多关于这方面的信息,基本上和之间的测试执行存在细微的差异@BeforeEach。对于,如果任何测试用例实现了存根@Before就足够了,对于,所有用例都必须这样做。@BeforeEach

如果您不想将设置分解@BeforeEach为许多小部分(正如上面引用的评论正确指出的那样),还有另一种选择,而不是激活整个测试类的宽松模式:您只需将这些存根放在该@BeforeEach方法宽松,单独使用lenient()


小智 5

代替

@RunWith(MockitoJUnitRunner.class)

@RunWith(MockitoJUnitRunner.Silent.class)

删除@RunWith(MockitoJUnitRunner.class)

或者只是注释掉不需要的模拟调用(显示为未经授权的存根)。