How to unit test callback logic?
More complete question is, given a dependency that expects a callback as a parameter, how do I write a unit test that covers the callback logic and still manage to mock up the dependency?
pu开发者_Python百科blic class DoStuff {
public void runThis(Runnable callback) {
// call callback
}
}
public class ClassUnderTest {
private DoStuff stuffToDo;
public void methodUnderTest() {
this.stuffToDo.runThis(/*a runnable with some logic*/)
}
}
In the example above, I would mock stuffToDo
since I should verify calls and mock outputs of method calls. However, mocking runThis
results in the callback logic not being tested. Furthermore, callback logic seems like it should be private so I wouldn't expect to test it directly; perhaps that is a misconception on my part.
Since callbacks are used rather extensivly, I would expect there to be a common method for testing them but I haven't found it.
You can't in a single test. If you mock something, its mocked, meaning it can only verify arguments and simulate return values (that you configure in the test).
Indeed, the entire point of mocking something is to test what uses the mock in isolation. If you want a unit test of DoStuff
, you don't want to have to worry about using some callback implementation that might or might not be working. You mock the callback so you don't have to worry about it.
You can still have good tests by testing the callback code in isolation, and testing the callback user in isolation (using a mock for the callback) and perhaps by throwing an integration test in for good measure, where you use the fully configured component as a whole.
Here basically you want to test the class DoStuff. That means you need to unti-test all the methods inside DoStuff, right?. So in this case, what you need to do is, instead of mocking stuffToDo itself, inject a mocked Runnable into stuffToDo. And then check whether your runnable was successfully executed or not.
But if you do have other functionalities in the class, you may have separate test and mock them out.
In your particular case, it sounds like you've already tested DoStuff
(or don't care anymore since it is mocked) and now are specifically unit-testing a specific Runnable
you've designed. In this case, callback
sounds like exactly what you want to be testing, in the same manner someone might want to specifically unit-test a database strategy or in-memory strategy directly.
If this is what you are attempting, either you can test it in a black box, exercising it through ClassUnderTest
as best as you can. Or you can create a testing harness on your particular Runnable. If you are releasing this code and don't want to make your testing harness reachable, you can make the test harness methods private and share its details specifically with your unit test assembly.
Look here for information about how to make friend assemblies. I typically sign my unit test code so that I don't have to muck with a command line compiler. I suppose it depends on your build environment.
How about a fake DoStuff that just invokes the Runnable unconditionally ?
Then you just sense for the effects - changes that should be observed if your callback was executed.
If your using EasyMock, you can use andStubAnswer
to call the runnable.
doSomethingMock.runThis(runnable);
expectLastCall().andStubAnswer(new IAnserable<Void>() {
Runnable runnable = (Runnable)getCurrentArguments()[0];
runnable.run();
return null;
});
I imagine other mocking frameworks contain something similar.
精彩评论