How can one test a function that does not return a value, but uses given arguments to construct some values which are being sent to a sequential function?
For example
public void handleSomeEvent(String str1, String str2){
MyObject obj1 = new MyObject();
obj1.setParameterA(str1);
obj2.setParameterB(str2);
EventHandler.getInstance().notify(obj1)
}
In the above example i would like to verify EventHandler.notify
was called with an object containing str1
as parameterA
and str2
as parameter2
. Can this be done using some common mocking frameowrk (i.e mockito)?
You can do following
@RunWith(PowerMockRunner.class)
@PrepareForTest({ EventHandler.class })
public class UnderTestTest {
@Test
public void testLoadUniqueOrNull() throws NKFException {
PowerMockito.mockStatic(EventHandler.class);
EventHandler handler = PowerMockito.mock(EventHandler.class);
PowerMockito.when(EventHandler.getInstance())
.thenReturn(handler);
ArgumentCaptor<MyObject> handlerArg =
ArgumentCaptor.forClass(MyObject.class);
PowerMockito.doNothing()
.when(handler)
.notify(handlerArg.capture());
new UnderTest().handleSomeEvent("test");
Assert.assertEquals(new MyObject("test"), handlerArg.getAllValues()
.get(0));
}
}
public class UnderTest {
public void handleSomeEvent(String str1) {
MyObject obj1 = new MyObject(str1);
EventHandler.getInstance()
.notify(obj1);
}
}
public class MyObject {
private final String x;
public MyObject(String x) {
this.x = x;
}
@Override
public boolean equals(Object obj) {
return ((MyObject) obj).x == x;
}
}
public class EventHandler {
private final static EventHandler e = new EventHandler();
public static EventHandler getInstance() {
return e;
}
public void notify(MyObject obj) {
// whatever
}
}
(side note this quick code is to demonstrate the functionality not best practises in coding)
In cases when need to verify called parrams use ArgumentCaptor<T>
by Mockito
See the reference:
https://static.javadoc.io/org.mockito/mockito-core/2.6.9/org/mockito/ArgumentCaptor.html
Unittest verify public observable behavior where "public" means through the units API and "behavior" meand return values and/or communication with dependencies.
Your code has a hidden dependecy intoduced by the static access to the EventHandler
object. I assume that your EventHandler
class incorporates the Java singelton pattern. This is STUPID code most of us have been started with.
You should not surrender to this bad design by using PowerMock.
The better way would be to pass an EventHandler
object as constructor parameter to your code under test like this:
class MyTestedClass{
private final EventHandler eventHandler;
class MyTestedClass(EventHandler eventHandler){
this.eventHandler=eventHandler;
}
public void handleSomeEvent(String str1, String str2){
MyObject obj1 = new MyObject();
obj1.setParameterA(str1);
obj2.setParameterB(str2);
eventHandler.notify(obj1)
}
}
And then make your EventHandler
a normal class that you can inherit from. (at least remofe the final
key word from the class declaration).
Then you could use plain Mockito to replace the EventHandler
object with a test double and verify your codes comunication with that test double:
class MyTestedClassTest{
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private EventHandler eventHandler; // ignores the private constructor
class MyTestedClass(EventHandler eventHandler){
this.eventHandler=eventHandler;
}
@Test
public void notifiesEventHandlerWithDTO(String str1, String str2){
new UnderTest(eventHandler).handleSomeEvent("test1","test2");
ArgumentCaptor<MyObject> handlerArg = ArgumentCaptor.forClass(MyObject.class);
verify(eventHandler).notify(handlerArg.capture());
assertThat("parameter A passed to object",
handlerArg.getValue().getParameterA(),
equalTo("test1"));
assertThat("parameter B passed to object",
handlerArg.getValue().getParameterB(),
equalTo("test2"));
}
}