Reputation: 10331
I have a class that normally runs in a thread that processes data forever until another thread invokes stop() on it. The problem I have is that the unit test gets stuck in the main loop since the test is single threaded and I want to keep it that way. How can I unit test this without polluting the code? this class is part of a critical system and needs to be as simple and efficient as possible so I want to avoid unit testing hacks in the code
public class MyClass implements Runnable {
boolean running;
public void run() {
//foo is injected from the outside
foo.start();
work();
foo.end();
}
public void work() {
running = true;
while(running) { //main loop
bar.process(); //bar is injected from the outside
}
}
public void stop() {
running = false;
}
}
Basically what I'm doing in the test is mocking out foo and bar and I call run()
from the unit test, where later I verify in the bar mock whether process
was actually called. I also verify that in the foo mock start()
and end()
got called. The problem is that because I really want to keep the test single threaded, the test thread gets stuck forever in the while(running)
loop.
Some things I have tried and don't like
stop()
on its call of process(). Mockito doesn't like the fact that I call another class' method and throws an exceptionAre there any other suggestions or common patterns to test Runnables, or maybe a better way to write my code so that testing it is easier?
Upvotes: 0
Views: 2201
Reputation: 131
Create an interface for the class of bar
that only contains the method process
. Your MyClass
seems generic enough so that this would be OK. Then, instead of mocking bar
, create your own implementation dummy (or mock, whatever you like to call it). This will then call the stop method and your process
method is only called once. You can check whether BarMock.process
was called with an assertion using its isCalled
method. Also, I would suggest an isRunning
method for your MyClass
so that you can check whether it was stopped.
public interface Processable {
public void process();
}
public class BarMock implements Processable {
private MyClass clazz;
private boolean called;
public BarMock(MyClass clazz) {
this.clazz = clazz;
called = false;
}
@Override
public void process() {
// you can assertTrue(clazz.isRunning()) here, if required
called = true;
clazz.stop();
}
public boolean isCalled() {
return called;
}
}
public class MyClass implements Runnable {
boolean running;
public void run() {
// foo is injected from the outside
foo.start();
work();
foo.end();
}
public void work() {
running = true;
while (running) { // main loop
bar.process(); // bar is injected from the outside
}
}
public void stop() {
running = false;
}
public boolean isRunning() {
return running;
}
}
I think this method has three advantages over the one suggested by William F. Jameson, but also disadvantages:
Advantages:
process
method was actually calledstop
method really stopsDisadvantages:
That said, I'd still prefer introducting the interface, since it doesn't pollute your code too much and therefore is a small price to pay.
Upvotes: 1
Reputation: 1843
I suggest making a change which would both make your code more by-the-book and allow breaking out in a single thread:
while (!Thread.currentThread().isInterrupted() && running) {
bar.process();
}
You can call Thread.currentThread().interrupt()
before you run this code; the thread's interrupted
flag will be set and the method isInterrupted()
will return true.
This is more by-the-book because it makes your main loop participate in Java's interruption mechanism.
Upvotes: 5