【问题标题】:Mockito: Is validation of framework usage enabled without @RunWith(MockitoJUnitRunner.class)Mockito:是否在没有 @RunWith(MockitoJUnitRunner.class) 的情况下启用了框架使用验证
【发布时间】:2023-03-09 18:43:01
【问题描述】:

我们正在使用 Testng 6.8.8 + Mockito 1.10.19,显然我们不能使用MockitoJUnitRunner,但验证框架仍然有效! 在这个thread 我读到它不应该是这样的。有人可以解释吗?这是因为我们还有@Before* 回调和MockitoAnnotations.initMocks(this)吗?

我的代码:

public class MealTransformerTest {

MealTransformer mealTransformer = new MealTransformer();

@Test(expectedExceptions = NotImplementedException.class)
public void shouldThrowException() throws NotImplementedException {
    mealTransformer.transform(any(),
            any(),
            any(),
            any());
}

} 在这个特定的测试中没有任何失败,但是当我运行该套件时,Mockito 会告诉我匹配器的错误使用。

我也可以这样做:

public class MealTransformerTest {

MealTransformer mealTransformer = new MealTransformer();

//I don't need it in the tests. But I need at least 1 @Mock or @Spy to trigger framework validation
@Mock
private CloneUtils cloneUtils;

@BeforeMethod
void setUp() {
    MockitoAnnotations.initMocks(this);
}


@Test(expectedExceptions = NotImplementedException.class)
public void shouldThrowException() throws NotImplementedException {
    mealTransformer.transform(any(),
            any(),
            any(),
            any());
}

@Test(expectedExceptions = NotImplementedException.class)
public void shouldThrowException123() throws NotImplementedException {
    mealTransformer.transform(any(),
            any(),
            any(),
            any());
}
}

我收到:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced argument matcher detected here:
....

不要误会我的意思,我真的很喜欢它的工作原理,但是看到没有@RunWith(MockitoJUnitRunner.class) 时我很惊讶。

【问题讨论】:

  • 您可以在参数匹配器中放置一个断点,看看为什么即使没有 MockitoJUnitRunner 仍然会生成此异常。即使没有运行器,如果使用不正确,Mockito 类仍然会抛出异常。跑步者只是在此之上提供了一些“集成级”验证。
  • MockitoJUnitRunner 用于 JUnit。 TestNG 将忽略它。因此,拥有与否不会随着 testng 测试而改变。
  • @JulienHerr,当然。我对此毫无疑问,我已经使用 testNG 和 JUnit 有一段时间了。
  • @nullpointer 提供指向我在问题中提到的同一线程的链接并不是很有帮助,但谢谢。

标签: java unit-testing junit mockito testng


【解决方案1】:

Matchers work via side-effects and static global state,所以你可以把这个调用分解成几部分,看看发生了什么。

MockitoAnnotations.initMocks(this);
// in @Before [1]

mealTransformer.transform(any(), any(), any(), any());
//              [6]       [2]    [3]    [4]    [5]

在 init [1] 之后,Java 看到对 any [2-5] 的四次调用和对 transform [6] 的一次调用。然而,Mockito 只看到对any 的四个调用;因为mealTransformer 不是mock,所以Mockito 看不到它的调用。如果您单独运行该测试,Mockito 将留下四个未被消耗的已记录匹配器。 JVM 将拆除测试框架,并且测试将通过——除非您在 @After 方法中调用了 validateMockitoUsage,这将完全捕获这种情况。

但是,当您有两个测试时,它们会像这样叠加起来:

MockitoAnnotations.initMocks(this);
// [1]
mealTransformer.transform(any(), any(), any(), any());
//              [6]       [2]    [3]    [4]    [5]
// test passes! now the next test
MockitoAnnotations.initMocks(this);
// [7]
mealTransformer.transform(any(), any(), any(), any());
//              [12]      [8]    [9]    [10]   [11]

这里,步骤 7 发生在与步骤 1-6 相同的 JVM 中的相同测试执行中,这意味着当堆栈上有 4 个未使用的匹配器时调用 initMocks。这绝不应该发生,所以initMocks 会第一次抓住这个错误。如果误用匹配器异常对应于失败的测试之外的匹配器testBtestA 中的匹配器误用而失败。如果在@After 消息中使用validateMockitoUsage,则相应的测试会失败。

【讨论】:

  • 感谢您的出色回答。我还提到(在第二个 sn-p 中的评论)我需要使用 @Mock/@Spy 来实现它。如果没有测试替身,则不会触发验证。你有机会看看stackoverflow.com/questions/10806345/… 吗?我只是在想:也许 testNG 不会为每个测试创建一个新的测试类实例这一事实是验证默认工作的原因? JUnit 会一遍又一遍地重新创建测试类,所以...
  • [CONTINUE] 也许这就是为什么没有 @RunWith(MockitoJUnitRunner.class) Mockito 不会触发 JUnit 测试的验证使用,仅仅是因为它无法存储其状态。
  • @yuranos87 我怀疑它比这更简单:在这两种情况下,只有在接下来与 Mockito 交互时才会得到验证,这意味着单次测试运行永远不会被验证。尽管 Mockito 相对擅长主动验证,但我敢打赌,调用 initMocks 而不创建任何模拟只是一个边缘情况,不会以实际验证的方式调用 mockspy。最后,请记住,模拟状态是全局的,但与调用它的线程相关联,因此如果测试发生在单独的线程上,它们可能无法获得您所询问的被动验证。
  • '只是一个边缘案例' - 哦,伙计,听起来很奇怪,考虑到 Mockito 现在的成熟度,它有一些边缘案例。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多