如何使用顺序调用测试 void 函数
How to test a void function with sequential calls
如何测试一个函数,它没有 return 一个值,而是使用给定的参数来构造一些被发送到顺序函数的值?
例如
public void handleSomeEvent(String str1, String str2){
MyObject obj1 = new MyObject();
obj1.setParameterA(str1);
obj2.setParameterB(str2);
EventHandler.getInstance().notify(obj1)
}
在上面的示例中,我想验证 EventHandler.notify
是使用包含 str1
作为 parameterA
和 str2
作为 parameter2
的对象调用的。这可以使用一些常见的模拟框架(即 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
}
}
(附注这个快速代码是为了演示功能而不是编码的最佳实践)
在需要验证被调用参数的情况下,使用 MockitoArgumentCaptor<T>
查看参考资料:
https://static.javadoc.io/org.mockito/mockito-core/2.6.9/org/mockito/ArgumentCaptor.html
Unittest 验证 public 可观察到的行为 其中 "public" 表示 通过单元 API 并且"behavior" 表示 return 值 and/or 与依赖项的通信.
您的代码具有 隐藏依赖性,由对 EventHandler
对象的 静态访问 引入。我假设您的 EventHandler
class 包含 Java 单身模式 。这是我们大多数人 STUPID code 的开始。
你不应该屈服于使用 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
成为您可以继承的普通 class。 (至少从 class 声明中删除 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"));
}
}
如何测试一个函数,它没有 return 一个值,而是使用给定的参数来构造一些被发送到顺序函数的值?
例如
public void handleSomeEvent(String str1, String str2){
MyObject obj1 = new MyObject();
obj1.setParameterA(str1);
obj2.setParameterB(str2);
EventHandler.getInstance().notify(obj1)
}
在上面的示例中,我想验证 EventHandler.notify
是使用包含 str1
作为 parameterA
和 str2
作为 parameter2
的对象调用的。这可以使用一些常见的模拟框架(即 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
}
}
(附注这个快速代码是为了演示功能而不是编码的最佳实践)
在需要验证被调用参数的情况下,使用 MockitoArgumentCaptor<T>
查看参考资料: https://static.javadoc.io/org.mockito/mockito-core/2.6.9/org/mockito/ArgumentCaptor.html
Unittest 验证 public 可观察到的行为 其中 "public" 表示 通过单元 API 并且"behavior" 表示 return 值 and/or 与依赖项的通信.
您的代码具有 隐藏依赖性,由对 EventHandler
对象的 静态访问 引入。我假设您的 EventHandler
class 包含 Java 单身模式 。这是我们大多数人 STUPID code 的开始。
你不应该屈服于使用 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
成为您可以继承的普通 class。 (至少从 class 声明中删除 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"));
}
}