简体   繁体   中英

Mockito verify fails in second unit test

I am using Mockito together to JUnit to implement unit tests for a class in an Android project.The problem is that I call Mockito.verify in two consequent tests where the tests are exactly same (to make sure that I am using Mockito correctly) but the interesting thing is that verify in second test always fails.I suspect that some operations need to be done before each test using @before annotation or so, that I have missed.Here are some code snippet about what I am doing.

I use Android Studio 3.4.1, Mockito 2.7.22 and 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();
}

Here comes the function under test

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();
        }
    }
}

I expect that both tests should pass without any problem. In fact, both tests are passed when I ran them individually, but they fail when I run the whole suite and the error is:

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.

I think the issue is a multithreading one. When you call initConnection , it calls mConnection.connect() in a Thread The problem you are having is that this Thread takes some time to complete and you end up calling verify(mockedZConnection, times(1)).connect(); before the Thread actually reached the connect() call. A way to make sure about it is to join the Thread after you start it, it will wait until the Thread has finished before continuing:

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

}

Now both tests should work.

This of course is not acceptable in the code, because it negated the use of a Thread . You will need an other way to test it.

A way I can think of would be to wait for the Thread to complete in your test before checking the mock:

@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();
}

Tried and it works fine for me. Not sure it is a best practice though as you need to make public some internals of your class, which violates encapsulation

maybe having a package protected isThreadAlive() method on your ZConnectionService class could be acceptable

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

and the loop in the test

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM