How to test a void function with sequential calls

2019-09-20 04:08发布

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)?

3条回答
我欲成王,谁敢阻挡
2楼-- · 2019-09-20 04:45

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)

查看更多
地球回转人心会变
3楼-- · 2019-09-20 04:59

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

查看更多
男人必须洒脱
4楼-- · 2019-09-20 05:01

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"));
        }
    }
查看更多
登录 后发表回答