Java 如何使用顺序调用测试void函数

Java 如何使用顺序调用测试void函数,java,unit-testing,mocking,mockito,powermockito,Java,Unit Testing,Mocking,Mockito,Powermockito,如果一个函数不返回一个值,但使用给定的参数来构造一些发送给顺序函数的值,那么如何测试该函数呢 比如说 public void handleSomeEvent(String str1, String str2){ MyObject obj1 = new MyObject(); obj1.setParameterA(str1); obj2.setParameterB(str2); EventHandler.getInstance().notify(obj1) } 在上面的示

如果一个函数不返回一个值,但使用给定的参数来构造一些发送给顺序函数的值,那么如何测试该函数呢

比如说

public void handleSomeEvent(String str1, String str2){
   MyObject obj1 = new MyObject();
   obj1.setParameterA(str1);
   obj2.setParameterB(str2);
   EventHandler.getInstance().notify(obj1)
}
在上面的示例中,我想验证是否使用包含
str1
as
parameterA
str2
as
parameter2
的对象调用了
EventHandler.notify
。这可以使用一些常见的模拟框架工作(即mockito)来完成吗?

您可以执行以下操作

@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
    }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({EventHandler.class})
公共类测试不足{
@试验
public void testLoadUniqueOrNull()引发NKFEException{
mockStatic(EventHandler.class);
EventHandler=PowerMockito.mock(EventHandler.class);
PowerMockito.when(EventHandler.getInstance())
.然后返回(处理程序);
ArgumentCaptor handlerArg=
ArgumentCaptor.forClass(MyObject.class);
PowerMockito.doNothing()
.何时(处理人)
.notify(handleArg.capture());
新的欠测试().handleSomeEvent(“测试”);
Assert.assertEquals(新的MyObject(“测试”),handleArg.getAllValues()
.get(0));
}
}
公共类测试不足{
public void handleSomeEvent(字符串str1){
MyObject obj1=新的MyObject(str1);
EventHandler.getInstance()
.通知(obj1);
}
}
公共类MyObject{
私有最终字符串x;
公共MyObject(字符串x){
这个.x=x;
}
@凌驾
公共布尔等于(对象obj){
返回((MyObject)obj).x==x;
}
}
公共类事件处理程序{
private final static EventHandler e=new EventHandler();
公共静态EventHandler getInstance(){
返回e;
}
公共无效通知(MyObject obj){
//随便
}
}

(旁注:此快速代码旨在演示功能,而非编码中的最佳实践)

如果需要验证调用的parrams,请使用Mockito的
ArgumentCaptor

见参考资料:

单元测试验证公共可观察行为,其中“公共”表示通过单元API,“行为”表示返回值和/或与依赖项通信

通过对
EventHandler
对象的静态访问,您的代码具有隐藏的依赖关系。我假设您的
EventHandler
类包含Java singelton模式。这是我们大多数人从一开始就有的

您不应该使用PowerMock屈服于这种糟糕的设计

更好的方法是将
EventHandler
对象作为构造函数参数传递给测试代码,如下所示:

    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)
        }
    }
然后使您的
EventHandler
成为可以继承的普通类。(至少从类声明中删除
final
关键字)

然后,您可以使用普通Mockito将
EventHandler
对象替换为测试双精度,并验证您的代码是否与该测试双精度通信:

    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"));
        }
    }
类MyTestedClassTest{
@规则public MockitoRule MockitoRule=MockitoJUnit.Rule();
@Mock private EventHandler EventHandler;//忽略私有构造函数
类MyTestedClass(EventHandler EventHandler){
this.eventHandler=eventHandler;
}
@试验
public void notifiedSeventhandlerWithDTO(字符串str1,字符串str2){
新的欠测试(eventHandler).handleSomeEvent(“test1”、“test2”);
ArgumentCaptor handlerArg=ArgumentCaptor.forClass(MyObject.class);
验证(eventHandler).notify(handlerArg.capture());
assertThat(“传递给对象的参数A”,
handlerArg.getValue().getParameterA(),
equalTo(“测试1”);
assertThat(“传递给对象的参数B”,
handlerArg.getValue().getParameterB(),
equalTo(“test2”);
}
}

编写本文时,您认为实际测试的内容可能重复?基本上你要测试的就是setter的工作。在这种情况下,
EventHandler
是冗余的。您也可以断言
obj1.getParamA().equals(str1)
etc@Lino-请重新阅读这两个线程,这不是同一个问题。@ABR我的观点是,这不是单元测试。您正在同时测试
MyObject
EventHandler
,而这两者都可以(也应该)独立测试。用mock
MyObject测试
EventHandler
。使用getter测试
MyObject
(如果你真的认为测试setter有价值的话,我不这么认为)。工作起来很有魅力!不过,不需要重写equals,handlerArg.getAllValues().get(0)EventHandler接收到的已退役MyObject,然后可以分别断言其属性(即str1和str2)。谢谢虽然你的答案在技术上是正确的,但它指导OP写作。我们最好让她写可靠的代码。。。