【问题标题】:Unit test which verifies only a few elements仅验证少数元素的单元测试
【发布时间】:2012-05-14 15:28:41
【问题描述】:

有一个接口:

interface IEventListener
{
  void onEvent(List <IEvent> events);
}

有一个事件类:

class EventB
{
  private final int id;
  private final A a;
  private final String somethingElse;
...
// constructors, getters
...
}

还有一个类要测试:

class Doer
{
 IEventListener eventListener;
 void doSomething(Aaa a)
 {
   eventListener.onEvent(Arrays.asList(new EventA(1), new EventC(2)));
...
   eventListener.onEvent(Arrays.asList(new EventB(42, a.getA(), "foo"), new EventA(3), new EventB(0, a.getA(), "bar")));
...
   eventListener.onEvent(Arrays.asList(new EventC(4)));
 }
}

Doer 是我需要测试的代码,doSomething 方法产生事件包,我需要测试它是否在某些特定条件下产生特定事件。

更准确地说,我想要一个单元测试,它调用方法 doSomething 并检查 EventB 是否使用“42”和 A 从方法参数 a 发送。应忽略所有其他事件。

为了进行这样的测试,我只提出了包含 ArgumentCaptor、for 块和魔术布尔标志的非常冗长代码的解决方案...

对其进行单元测试的最佳方法是什么?可能是代码设计不好?

【问题讨论】:

    标签: java unit-testing junit mockito


    【解决方案1】:

    设计是正确的,这就是你用 Mockito 测试它的方式:

    import org.hamcrest.Matchers;
    import org.mockito.Mockito;
    public void firesEventsOnDoSomething() {
      Listener listener = Mockito.mock(Listener.class);
      Doer doer = new Doer(listener);
      doer.doSomething(aaa);
      Mockito.verify(listener).onEvent(
        Mockito.argThat(
          Matchers.hasItem(
            Matchers.allOf(
              Matchers.instanceOf(EventB.class),
              Matchers.hasProperty("a", Matchers.equalTo(aaa.getA())),
              // whatever you want
            )
          )
        )
      );
    }
    

    它是 Mockito 1.9.0 和 Hamcrest-library 1.2.1。

    要将 JUnit 4.10 与 Hamcrest-library 1.2.1 一起使用,您应该使用 junit:junit-dep:4.10 artifact,并从中排除 org.hamcrest:hamcrest-core

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit-dep</artifactId>
      <version>4.10</version>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.hamcrest</groupId>
          <artifactId>hamcrest-core</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-core</artifactId>
      <version>1.2.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>1.2.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>1.9.0</version>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.hamcrest</groupId>
          <artifactId>hamcrest-core</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    

    【讨论】:

    • 谢谢。它看起来很有希望。但是,正如我所见,它需要使用包含事件的列表调用 onEvent。但正如您在我的代码 sn-p 中看到的那样,onEvent 被多次调用,并且只有一次它具有所需的事件。任何想法如何改进它?
    • hamcrest 也不适用于 junit 4.10:NoSuchMethodError: org.hamcrest.core.AllOf.allOf(Lorg/hamcrest/Matcher;Lorg/hamcrest/Matcher;Lorg/hamcrest/Matcher;)Lorg/hamcrest/Matcher;
    • 我认为它会完全按照您的需要工作。 Mockito 将验证在带有 require 参数的执行中发生了。只要所需的执行到位,所有其他执行都将被忽略。请参阅我对 mockito+junit+hamcrest 组合的更新答案。
    • 顺便说一句,不需要使用&lt;exclusion&gt;,因为上面明确包含了hamcrest-core工件依赖项。
    • 如果你避免使用&lt;exclusion&gt; 元素,那么依赖的顺序就会很重要。这意味着hamcrest-core 必须始终高于junit-dep,这是个坏主意。
    【解决方案2】:

    如果您使用的是 JUnit4,您可以尝试参数化测试。这里有一个例子http://www.mkyong.com/unittest/junit-4-tutorial-6-parameterized-test/

    如果您必须对每个参数与不同的结果进行比较,则最好将它们视为不同的测试用例。

    【讨论】:

    • Doer 是我需要测试的代码,doSomething 方法只是产生事件包,我需要测试它是否在某些特定条件下产生特定事件。你的回答不相关。
    • 如果问题是模拟事件,你可以关注这个线程stackoverflow.com/questions/9905928/mockito-testing-events
    • 是的,我知道ArgumentCaptor,但也许有更好的方法,您发送的链接有一个简单的情况,即只发送单个事件。我有包...
    【解决方案3】:

    创建EventListener的虚拟实现:

    class DummyEventListener implements EventListener {
        private int expectedId;
        DummyEventListener(int expectedId) {
           this.expectedId = expectedId;
        }
        void onEvent(List <IEvent> events) {
            for (IEvent event : events) {
                if (!(event instanceof EventB)) {
                    continue;
                }
                EventB eb = (EventB)event;
                assertEquals(expectedId, eb.getId());
                // add more asserts here
            }
        }
    }
    

    或者,您可以使用一种可用的 java 模型框架: EasyMock、JMock、Mockito、JMockit 等。

    【讨论】:

    • 我使用 Mockito。如何用 Mockito 做到这一点? (或者如果需要,我可以切换到任何其他框架)。顺便说一句,您的代码根本不会检查此类事件是否存在。需要更多代码……它让单元测试看起来很糟糕……
    猜你喜欢
    • 2015-10-01
    • 1970-01-01
    • 2017-05-23
    • 2010-11-19
    • 1970-01-01
    • 2019-12-18
    • 2011-01-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多