Java Mockito 一个测试导致另一个失败

Java Mockito one test causes another to fail

@Test
    public void onConnectionCompletedTest() {
        connectionProvider.initialize();
        connectionProvider.addEventObserver(SocketEvent.Type.SOCKET_CONNECT, mockedObserver);
        connectionProvider.onConnectionCompleted(mockedChannel);

        verify(mockedObserver).socketEventObserved(socketEventCaptor.capture());
        Assert.assertEquals(SocketEvent.Type.SOCKET_CONNECT, socketEventCaptor.getValue().getType());
    }


@Test
    public void onConnectionClosedTest() {
        connectionProvider.initialize();
        connectionProvider.addEventObserver(SocketEvent.Type.SOCKET_DISCONNECT, mockedObserver);
        connectionProvider.onConnectionClosed(mockedChannel);

        verify(mockedObserver).socketEventObserved(socketEventCaptor.capture());
        Assert.assertEquals(SocketEvent.Type.SOCKET_DISCONNECT, socketEventCaptor.getValue().getType());
    }

问题是当我 运行 这两个测试时,第二个失败了。但是如果我注释掉

verify(mockedObserver).socketEventObserved(socketEventCaptor.capture());
        Assert.assertEquals(SocketEvent.Type.SOCKET_CONNECT, socketEventCaptor.getValue().getType());

那么第二个测试就通过了。这涉及到很多不同的 classes/methods,所以希望这些信息足以让我们做出解释。

我得到的错误:

wanted but not invoked:
mockedObserver.socketEventObserved(
    <Capturing argument>
);
-> at com.company.cnx.cip.io.ConnectionProviderTest.onConnectionClosedTest(ConnectionProviderTest.java:188)
Actually, there were zero interactions with this mock.

我的问题是:当我@Ignore 第一个测试时,第二个测试会通过,会发生什么?

编辑:我有一个重要的@Before class。

@Before
    public void init() {
        MockitoAnnotations.initMocks(this);
        JsonParser parser = new JsonParser();
        JsonElement jsonElement = parser.parse(json);
        configurationService.loadConfiguration(jsonElement, "id");
        AppContext.getContext().applyConfiguration(configurationService);
        connectionProvider = ConnectionProvider.newInstance();

    }

您可以忽略第一行和最后一行以外的所有内容。我创建了一个新的 ConnectionProvider 对象,所以我认为一个测试不应该影响另一个,因为它们在两个单独的对象上运行。

这比您想象的更直接:当您注释掉这些行时,测试会起作用,因为 socketEventObserved 在 运行 的第二次测试时不会被调用。这可能是您未在上面发布的代码的问题。

由于看起来罪魁祸首可能隐藏在不切实际的代码量中,这里有一些调试技巧和测试污染的一般来源:

  • 首先在调用socketEventObserved的地方打个断点,比较单测运行和多测运行。如果您看到 Mockito 看到的相同行为,那么它不是 Mockito。

  • 在这种情况下,请留意其他线程(尤其是侦听器)上可能发生的操作。使用 Mockito timeout 可以提供帮助:

    verify(mockedObserver, timeout(2000))
        .socketEventObserved(socketEventCaptor.capture());
    
  • 您似乎正在使用 I/O 通道,这有时涉及缓冲或刷新策略,只有当一定数量的测试是 运行 时才能触发,或者如果测试按特定顺序 运行。确保您的 @Before 方法完全重置状态,包括您的代码可能触及的任何模式或缓冲区。

  • 您与 AppContext.getInstance()ConnectionProvider.newInstance() 交互,它们都是静态方法调用。我不太担心后者,除非它不顾名称保留实例,但前者可能不会友好地进行多次初始化。通常,在您的被测系统中,请留意对全局状态的写入。

  • Mockito 本身保持静态。 Mockito 保持其内部状态静态和线程作用域(通过 ThreadLocal),有时测试会使 Mockito 处于它无法检测到的无效内部状态(例如,因为操作完成了一半)。添加一个@After 方法来检测这种情况:

    @After public void checkMockito() { Mockito.validateMockitoUsage(); }
    

    ...或切换到使用 MockitoRuleMockitoJUnitRunner,它们会自动执行此操作并 initMocks(this)

  • 最后,Mockito 并不完美:它使用代理对象来覆盖方法,这也意味着它会默默地模拟 final 方法和一些非 public 方法失败使用嵌套 类 的方法(因为这些方法需要编译器生成您看不到的合成方法)。如果您的 mockedObserver 有任何最终或有限可见性方法,它可能会导致真实代码和模拟代码以一种使系统行为难以预测的方式进行交互。