java-addict301
java-addict301

Reputation: 4136

How to mock Thread.class with Mockito?

As part of my unit tests, I am trying to mock the Thread.class isAlive() method to return true using Mockito. Below is my code:

final Thread mockThread = Mockito.mock(Thread.class);
Mockito.when(mockThread.isAlive()).thenReturn(true);

This is giving me the following exception on the second line:

Exception in thread "main" org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'. For example: when(mock.getArticles()).thenReturn(articles); Also, this error might show up because: 1. you stub either of: final/private/equals()/hashCode() methods. Those methods cannot be stubbed/verified. Mocking methods declared on non-public parent classes is not supported. 2. inside when() you don't call method on mock but on some other object.

I have used Mockito many times this way without issue. Is there some issue with mocking Thread.class? I have searched around with no luck.

Upvotes: 4

Views: 19834

Answers (4)

davidxxx
davidxxx

Reputation: 131456

Thread.isAlive() is declared with the final modifier. It cannot be mocked by Mockito 1.
Use Powermock or Mockito 2 that adds this feature :

For a long time our users suffered a disbelief when Mockito refused to mock a final class. ... Mocking of final classes and methods is an incubating, opt-in feature

Or another way : change the way to unit test your class.

Do you really need to rely on isAlive() to unit test ?

If you really need it, you could also use a wrapper class that composes a Thread instance and that delegates the processing to the composed Thread.
This could implement Runnable for example.
In this way you could naturally mock the isAlive() method of this wrapper class.

Upvotes: 3

GhostCat
GhostCat

Reputation: 140523

I give you a completely different perspective: get rid of interacting with threads in your production code completely.

Thing is: Thread is a pretty low level abstraction for multi-threading. 10, 15 years back we had only Threads, and where thus forced to use them.

But nowadays, you have a huge variety of abstractions that work on a higher level, like ExecutorServices, and Futures, and Promises, and ...

It might sound strange - but it might be more helpful for you to step back and consider not using threads the way you are using them today. You see, there are things like Same-Thread-Executors that allow you to turn your multi-threaded code into single threaded for testing - just by providing a different service to execute tasks.

That is the level of abstraction that you should strive for in 2017 - you shouldn't waste time on mocking low level final methods!

Upvotes: 2

java-addict301
java-addict301

Reputation: 4136

As others have pointed out, Thread's isAlive method is final and therefore cannot (and probably should not) not be mocked using Mockito.

A simple workaround was to create a private Runnable class in my unit test class with a run() method that just calls this.wait().

private class TestRunnable implements Runnable {
        @Override
        public void run() {
            synchronized(this) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    System.out.println("Interrupted!");
                }
            }       
        }
    }

I am then able to create a thread in the unit test using an instance of this and the isAlive() method will always return true.

final TestRunnable testRunnable = new TestRunnable();
final Thread expectedThread = new Thread(testRunnable);
expectedThread.start();

Then later in the test..

assertTrue(expectedThread.isAlive());

And finally, To cleanup the thread, just call notify() on the Runnable instance (of course the thread will end when JUnit finishes too anyway)

synchronized(testRunnable) {
    testRunnable.notifyAll(); // kill the Runnable (prob not necessary)
}

Upvotes: 3

Kyle Scott
Kyle Scott

Reputation: 64

It looks like the Thread.isAlive method is final, and final methods can not be mocked with Mockito.

Here is a related stackoverflow post.

Upvotes: 1

Related Questions