如何模拟事件处理程序?

时间:2015-08-25 20:57:21

标签: java events junit mockito

我为Stash写了一个事件处理程序,通过消息传递总线架构发送消息。以下是我fedmsgEventListener课程中的一个示例:

@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);
}

它获取一个事件,从中提取信息,根据事件中的信息构造一个主题,并调用一个方法来发送消息。我需要为所有这些事件处理程序编写单元测试。

这是运行我试图实现的第一个测试的类:

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"));
    }
}

然后这是测试调用的类和方法:

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,因为fedmsgEventListenerPullRequestEvent都是模拟对象,因此为null。

这是进行单元测试的最佳方式吗?从高层次来看,这就是我想做的事情:触发事件,看到主题变为包含某个字符串的字符串。

1 个答案:

答案 0 :(得分:4)

你使用的Mockito完全错了。抱歉。首先,@Mock在不使用initMocksMockitoJUnitRunner的情况下无法正常工作,但无论如何我都不会这样做。模拟不为空;你应该可以在模拟上调用方法;在你的情况下,你没有初始化/创建模拟,这就是它们为空的原因。

首先,确定您尝试测试的课程。它看起来像是FedmsgEventListener。然后,使用模拟对象和数据结构而不是具有依赖性的真实对象等与该类的真实实例进行交互。注意,我在这里使用Hamcrest 1.3

基于模拟的测试在three phases中构建:

  1. 创建 - 创建您的模拟,然后说明&#34;当&#34;发生与模拟的互动,做点什么。
  2. 互动 - 以您尝试测试的方式与您的对象互动。
  3. 验证 - 使用Mockito.verify和JUnit / Hamcrest assert方法确保事情按预期方式发挥作用。
  4. 您可能会这样做:

    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();
      }
    }
    

    这段代码可能并不完全符合您的需要,但您还没有向我展示足够的代码,而您正试图为我测试以完全正确。但是,我认为这应该足以让你开始。

    顺便说一句,如果您无法使用模拟依赖项创建类的真实实例,那么这就是代码气味,您的代码应该重构。这就是为什么静态是如此糟糕的一个原因,因为如果你的代码通过静态访问全局状态,那么你必须用你的静态设置全局状态。使您的类能够使用模拟依赖项,将它们作为参数传递给构造函数,使用when指定模拟行为,然后断言/验证结果。