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

标签 java android unit-testing junit mockito

我将 Mockito 与 JUnit 一起使用来为 Android 项目中的类实现单元测试。问题是我在两个后续测试中调用 Mockito.verify ,其中测试完全相同(到确保我正确使用 Mockito),但有趣的是,第二次测试中的验证总是失败。我怀疑在每次测试之前需要使用 @before 注释完成一些操作,所以我错过了。这里有一些关于我正在做的事情的代码 fragment 。

我使用 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.

最佳答案

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

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

}

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

这在代码中当然是 Not Acceptable ,因为它否定了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();
}

试过了,对我来说效果很好。不确定这是最佳实践,因为您需要公开类的一些内部结构,这违反了封装

也许您的 ZConnectionService 类上有一个受包保护的 isThreadAlive() 方法是可以接受的

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

以及测试中的循环

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

关于java - Mockito 验证在第二个单元测试中失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56390990/

相关文章:

java程序使用邮件中指定的url

java - Spring-MVC 中的 REST 调用

android - 无需配对的蓝牙连接

android - 如何将 Room Persistence Library 导入 Android 项目

java - 在 Google Guice 中自动启动和停止服务

android - 浏览多个 Activity

javascript - angularjs:指令的访问 Controller

c# - 编写 unittest 以创建一个文件,然后删除该文件

php - 我可以使用@depends 来依赖使用@dataProvider 的测试吗?

Java等价于iif函数