Mockito验证在第二个单元测试中失败

时间:2019-05-31 07:56:49

标签: java android unit-testing junit mockito

我正在一起使用Mockito和JUnit来为Android项目中的类实现单元测试。问题是我在两个随后的测试中调用了Mockito.verify,这些测试完全相同(以确保我是正确使用Mockito),但有趣的是第二次测试中的验证始终会失败。我怀疑每次使用@before批注进行测试之前都需要做一些操作,而我已经错过了。以下是一些代码段我在做什么。

我使用Android Studio 3.4.1,Mockito 2.7.22和JUnit 4.12。

@Test
public void test_onStart_do_nothing() throws Exception {
    ZConnectionService zConnectionService = new ZConnectionService();
    ZConnection mockedZConnection = mock(ZConnection.class);

    doNothing().when(mockedZConnection).connect();
    zConnectionService.initConnection(mockedZConnection);

    verify(mockedZConnection, times(1)).connect();
}

@Test
public void test_onStart_throw_IO_exceptioon() throws Exception {
    ZConnectionService zConnectionService = new ZConnectionService();
    ZConnection mockedZConnection = mock(ZConnection.class);

    doNothing().when(mockedZConnection).connect();
    zConnectionService.initConnection(mockedZConnection);
    // Line above is the line that error message points to!

    verify(mockedZConnection, times(1)).connect();
}

正在测试的功能

public void initConnection(ZConnection connection) {
    Log.d(TAG,"initConnection()");

    if (mConnection == null) {
        mConnection = connection;
    }

    if (!mActive) {
        mActive = true;

        if (mThread == null || !mThread.isAlive()) {
            mThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    // The code here runs in a background thread.
                    Looper.prepare();
                    mTHandler = new Handler();

                    try {
                        mConnection.connect();
                    } catch (IOException e) {
                        Intent i = null;
                        i = new Intent(ZConnectionService.UI_NOTCONNECTED);
                        i.setPackage(getApplicationContext().getPackageName());
                        getApplicationContext().sendBroadcast(i);

                        e.printStackTrace();

                        // Stop the services all together.
                        stopSelf();
                    }

                    Looper.loop();
                }
            });

            mThread.start();
        }
    }
}

我希望两项测试都可以顺利通过。实际上,当我分别运行它们时,两个测试都通过了,但是当我运行整个套件时它们都失败了,错误是:

Wanted but not invoked:
mockedZinkConnection.connect();
-> at com.app.z.ZConnectionServiceUnitTest.test_onStart_throw_IO_exceptioon(ZConnectionServiceUnitTest.java:207)
Actually, there were zero interactions with this mock.

1 个答案:

答案 0 :(得分:1)

我认为这个问题是多线程的。 当您呼叫initConnection时,它会在mConnection.connect()中呼叫Thread 您遇到的问题是,此Thread需要花费一些时间才能完成,并且最终您会在线程实际到达verify(mockedZConnection, times(1)).connect();调用之前调用connect()。 确保它的一种方法是在启动Thread之后加入它,它会等到Thread完成后再继续:

mThread.start();
try {
    mThread.join();
} catch (InterruptedException i) {
    i.printStackTrace();

}

现在两个测试都应该可以工作。

这在代码中当然是不可接受的,因为它否定了Thread的使用。您将需要另一种方法进行测试。

我能想到的一种方法是在测试模拟之前等待线程完成:

@Test
public void test_onStart_throw_IO_exceptioon() throws Exception {
    ZConnectionService zConnectionService = new ZConnectionService();
    ZConnection mockedZConnection = mock(ZConnection.class);
    doNothing().when(mockedZConnection).connect();

    zConnectionService.initConnection(mockedZConnection);

    // Wait for the Thread to complete
    while(zConnectionService.mThread.isAlive()) {
        Thread.sleep(100);
    }
    verify(mockedZConnection, times(1)).connect();
}

尝试过,对我来说很好。但是,由于您需要公开类的某些内部结构(违反封装

),因此不确定是否是最佳做法

在您的isThreadAlive()类上使用受软件包保护的ZConnectionService方法是可以接受的

boolean isThreadAlive() {
    return mThread.isAlive();
}

和测试中的循环

while(zConnectionService.isThreadAlive()) {
    Thread.sleep(100);
}