如何使用jMockit在单元测试中调用实际函数?

时间:2018-02-07 12:01:23

标签: java unit-testing jmockit

我已阅读此帖:Is there a way in JMockit to call the original method from a mocked method?

但推荐的解决方案会引发NPE。这是我的来源

 static Map<String, Boolean> detectDeadlocks(int timeInSeconds) {
    final Map<String, Boolean> deadlockMap = new LinkedHashMap<>();
    new Timer().schedule(new TimerTask() {

      @Override
      public void run() {
        // I want to check if the method is run.
        **deadlockMap.put("deadlock", isDeadlockAfterPeriod());**
      }

    }, timeInSeconds * 1000);
    return deadlockMap;
  }

我希望能够在单元测试中调用isDeadlockAfterPeriod。这是我在单元测试中嘲笑的静态方法。

我的单元测试代码

  @Test
  public void testDetectDeadlocks() throws Exception {
    new Expectations(){
      {
           // Called from inside the TimerTask.
           ClassUnderTest.isDeadlockAfterPeriod();
           result = false;
      }
    };

    TimerMockUp tmu = new TimerMockUp();
    Deadlocks.detectDeadlocks(0);
    Assert.assertEquals(1, tmu.scheduleCount);
  }

  class TimerMockUp extends MockUp<Timer> {
    int scheduleCount = 0;

    @Mock
    public void $init() {}

    @Mock
    public void schedule(Invocation invocation, TimerTask task, long delay) {
      scheduleCount ++;
      invocation.proceed(); // Trying to call the real method, but this throws a NPE.
    }
  }

Eclipse中的JUnit可以看到错误堆栈跟踪。

java.lang.NullPointerException
    at com.myproject.test.DeadlocksTest$TimerMockUp.schedule(DeadlocksTest.java:78)
    at com.myproject.test.Deadlocks.detectDeadlocks(Deadlocks.java:41)
    at com.myproject.test.DeadlocksTest.testDetectDeadlocks(DeadlocksTest.java:86)

1 个答案:

答案 0 :(得分:0)

你的问题是你也伪造了Timer的构造函数(而不仅仅是schedule方法)。

通过这样做,您正在阻止Timer的正确初始化,并且当您正在使用其实际实现时,它无法这样做。

具体而言(使用我拥有的资源),您阻止了queue字段的初始化,该mainLoop()字段用于TimerTask.run()方法(将调用Deadlocks的方法)。

此外,您需要对isDeadlockAfterPeriod类进行部分模拟,因为我知道public class Deadlocks { public static Map<String, Boolean> detectDeadlocks(int timeInSeconds) { final Map<String, Boolean> deadlockMap = new LinkedHashMap<>(); new Timer()// this will be the real constructor .schedule( // this will be first mocked, then really executed new TimerTask() { @Override public void run() { deadlockMap.put("deadlock", isDeadlockAfterPeriod()); // this will put false after the mock is called } }, timeInSeconds * 1000); return deadlockMap; } public static Boolean isDeadlockAfterPeriod() { return true; // this, we will mock it } } 也是该类的静态方法。

我会在这里留下一个有效的例子:

<强> Deadlocks.class

@RunWith(JMockit.class)
public TestClass{
    @Test
    public void testDetectDeadlocks() throws Exception {
        new Expectations(Deadlocks.class){ // do partial mocking of the Deadlock class
            {
                // Called from inside the TimerTask.
                Deadlocks.isDeadlockAfterPeriod();
                result = false;
            }
        };

        // prepare the fake
        TimerMockUp tmu = new TimerMockUp();

        // execute the code
        Map<String, Boolean> result = Deadlocks.detectDeadlocks(0);

        // assert results
        assertThat(tmu.scheduleCount, is(1));
        assertThat(result.size(), is(1));
        assertThat(result.get("deadlock"), is(false));
    }

    class TimerMockUp extends MockUp<Timer> {
        int scheduleCount = 0;

        @Mock
        public void schedule(Invocation invocation, TimerTask task, long delay) {
            scheduleCount ++;

            invocation.proceed();
        }
    }
}

测试类

dict

通常,在伪装构造函数时要非常小心,因为可能会使实例处于不一致状态。