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

时间:2016-06-14 17:25:57

标签: java mockito assert

@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());
然后第二次测试将通过。这里涉及很多不同的类/方法,所以希望能够提供足够的信息来解释。

我得到的错误:

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

我的问题确切地说:当我在@Ignore第一次测试时,第二次测试会通过时会发生什么?

编辑:我有一个非常重要的@Before课程。

@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对象,所以我认为一个测试不会影响另一个,因为它们会在两个独立的对象上运行。

1 个答案:

答案 0 :(得分:3)

这比你想象的要简单明了:当你注释掉那些行时,测试会起作用,因为当它是运行的第二次测试时,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具有任何最终或有限可见性方法,则可能会导致实际代码和模拟代码以使系统行为难以预测的方式进行交互。