我如何模拟事件处理程序?
How do I mock an Event Handler?
我为 Stash 编写了一个事件处理程序,以通过消息传递总线架构发送消息。这是我的 fedmsgEventListener
class:
中的一个示例
@EventListener
public void opened(PullRequestOpenedEvent event)
{
HashMap<String, Object> message = prExtracter(event);
String originProjectKey = ((HashMap<String, Object>)message.get("source")).get("project_key").toString();
String originRepo = ((HashMap<String, Object>)message.get("source")).get("repository").toString();
String topic = originProjectKey + "." + originRepo + ".pullrequest.opened";
sendMessage(topic, message);
}
它获取一个事件,从中提取信息,根据事件中的信息构造一个主题,并调用一个方法来发送消息。我需要为所有这些事件处理程序编写单元测试。
这是运行我尝试实施的第一个测试的 class:
import org.junit.Test;
import com.cray.stash.MyPluginComponent;
import com.cray.stash.MyPluginComponentImpl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MyComponentUnitTest
{
@Test
public void testMyName()
{
MyPluginComponent component = new MyPluginComponentImpl(null);
assertTrue(component.openPullRequest().contains(".pullrequest.opened"));
}
}
然后这里是测试调用的 class 和方法:
import com.atlassian.sal.api.ApplicationProperties;
import com.atlassian.stash.event.pull.*;
import org.mockito.Mock;
import static org.mockito.Mockito.*;
public class MyPluginComponentImpl implements MyPluginComponent
{
@Mock private PullRequestEvent event;
@Mock private PullRequestOpenedEvent opened;
@Mock private FedmsgEventListener fedmsgEventListener;
public MyPluginComponentImpl(ApplicationProperties applicationProperties)
{
this.applicationProperties = applicationProperties;
}
public String openPullRequest()
{
fedmsgEventListener.opened(opened);
return fedmsgEventListener.getTopic();
}
}
截至目前,该方法抛出 NullPointerException
,因为 fedmsgEventListener
和 PullRequestEvent
都是模拟对象,因此为 null。
这是对这种情况进行单元测试的最佳方式吗?从高层次上看,这就是我想要做的:触发事件,看到主题已更改为包含特定字符串的字符串。
您使用的 Mockito 完全错误。对不起。首先,如果不使用 initMocks
或 MockitoJUnitRunner
,@Mock
将无法工作,但无论如何我都不会那样做。模拟是 not null;您应该能够在模拟上调用方法;在您的情况下,您没有初始化/创建模拟,这就是它们为空的原因。
首先,确定您要测试的class。这里好像是FedmsgEventListener
。然后,使用模拟对象和数据结构而不是具有依赖性的真实对象等,与 class 的 真实 实例进行交互。注意,我在这里使用 Hamcrest 1.3。
在 three phases:
中构建了基于模拟的测试
- 创建 - 创建你的模拟,然后声明 "when" 与该模拟发生交互,做一些事情。
- 交互 - 以您尝试测试的方式与对象交互。
- Verify - 使用
Mockito.verify
和 JUnit/Hamcrest assert
方法来确保事情按照您预期的方式进行。
你可能会这样做:
import static org.mockito.Mockito.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
private HashMap<String, Object> createMessageDetails(String project_key, String repository) {
HashMap<String, Object> details = new HashMap<>();
details.put("project_key", project_key);
details.put("repository", repository);
return details;
}
public class FedmsgEventListenerTest {
@Test
public void testOpened() {
// when
PullRequestOpenedEvent event = mock(PullRequestOpenedEvent.class);
when(event.someMethodForPrExtracterYouHaventShownMe()).thenReturn(createMessageDetails("myKey", "myRepo"));
// then
FedmsgEventListener listener = new FedmsgEventListener();
listener.opened(event);
// verify
assertThat(event.getTopic(), containsString(".pullrequest.opened"));
verify(event).someMethodForPrExtracterYouHaventShownMe();
}
}
这段代码可能不是您所需要的,但您没有向我展示您要测试的代码,以确保它完全正确。但是,我认为这应该足以让您入门。
顺便说一句,如果您无法创建具有模拟依赖项class 的真实实例,那么这就是代码异味和您的代码应该重构。这就是为什么静态是一个坏主意的原因之一,因为如果您的代码通过静态访问全局状态,那么您必须使用静态设置全局状态。使您的 class 能够使用模拟依赖项,将它们作为参数传递给构造函数,使用 when
指定模拟行为,然后断言/验证结果。
我为 Stash 编写了一个事件处理程序,以通过消息传递总线架构发送消息。这是我的 fedmsgEventListener
class:
@EventListener
public void opened(PullRequestOpenedEvent event)
{
HashMap<String, Object> message = prExtracter(event);
String originProjectKey = ((HashMap<String, Object>)message.get("source")).get("project_key").toString();
String originRepo = ((HashMap<String, Object>)message.get("source")).get("repository").toString();
String topic = originProjectKey + "." + originRepo + ".pullrequest.opened";
sendMessage(topic, message);
}
它获取一个事件,从中提取信息,根据事件中的信息构造一个主题,并调用一个方法来发送消息。我需要为所有这些事件处理程序编写单元测试。
这是运行我尝试实施的第一个测试的 class:
import org.junit.Test;
import com.cray.stash.MyPluginComponent;
import com.cray.stash.MyPluginComponentImpl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MyComponentUnitTest
{
@Test
public void testMyName()
{
MyPluginComponent component = new MyPluginComponentImpl(null);
assertTrue(component.openPullRequest().contains(".pullrequest.opened"));
}
}
然后这里是测试调用的 class 和方法:
import com.atlassian.sal.api.ApplicationProperties;
import com.atlassian.stash.event.pull.*;
import org.mockito.Mock;
import static org.mockito.Mockito.*;
public class MyPluginComponentImpl implements MyPluginComponent
{
@Mock private PullRequestEvent event;
@Mock private PullRequestOpenedEvent opened;
@Mock private FedmsgEventListener fedmsgEventListener;
public MyPluginComponentImpl(ApplicationProperties applicationProperties)
{
this.applicationProperties = applicationProperties;
}
public String openPullRequest()
{
fedmsgEventListener.opened(opened);
return fedmsgEventListener.getTopic();
}
}
截至目前,该方法抛出 NullPointerException
,因为 fedmsgEventListener
和 PullRequestEvent
都是模拟对象,因此为 null。
这是对这种情况进行单元测试的最佳方式吗?从高层次上看,这就是我想要做的:触发事件,看到主题已更改为包含特定字符串的字符串。
您使用的 Mockito 完全错误。对不起。首先,如果不使用 initMocks
或 MockitoJUnitRunner
,@Mock
将无法工作,但无论如何我都不会那样做。模拟是 not null;您应该能够在模拟上调用方法;在您的情况下,您没有初始化/创建模拟,这就是它们为空的原因。
首先,确定您要测试的class。这里好像是FedmsgEventListener
。然后,使用模拟对象和数据结构而不是具有依赖性的真实对象等,与 class 的 真实 实例进行交互。注意,我在这里使用 Hamcrest 1.3。
在 three phases:
中构建了基于模拟的测试- 创建 - 创建你的模拟,然后声明 "when" 与该模拟发生交互,做一些事情。
- 交互 - 以您尝试测试的方式与对象交互。
- Verify - 使用
Mockito.verify
和 JUnit/Hamcrestassert
方法来确保事情按照您预期的方式进行。
你可能会这样做:
import static org.mockito.Mockito.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
private HashMap<String, Object> createMessageDetails(String project_key, String repository) {
HashMap<String, Object> details = new HashMap<>();
details.put("project_key", project_key);
details.put("repository", repository);
return details;
}
public class FedmsgEventListenerTest {
@Test
public void testOpened() {
// when
PullRequestOpenedEvent event = mock(PullRequestOpenedEvent.class);
when(event.someMethodForPrExtracterYouHaventShownMe()).thenReturn(createMessageDetails("myKey", "myRepo"));
// then
FedmsgEventListener listener = new FedmsgEventListener();
listener.opened(event);
// verify
assertThat(event.getTopic(), containsString(".pullrequest.opened"));
verify(event).someMethodForPrExtracterYouHaventShownMe();
}
}
这段代码可能不是您所需要的,但您没有向我展示您要测试的代码,以确保它完全正确。但是,我认为这应该足以让您入门。
顺便说一句,如果您无法创建具有模拟依赖项class 的真实实例,那么这就是代码异味和您的代码应该重构。这就是为什么静态是一个坏主意的原因之一,因为如果您的代码通过静态访问全局状态,那么您必须使用静态设置全局状态。使您的 class 能够使用模拟依赖项,将它们作为参数传递给构造函数,使用 when
指定模拟行为,然后断言/验证结果。