如何在 JMockit 中用 @Injectable 注释的类实例中调用真实方法



我正在寻找一种在JMockit中注入类内部私有字段的方法,同时保持触发真实方法的能力。我使用JMockit提供的@Injectable@Tested。但不知何故,注入的实例无法调用真正的方法。

示例测试:

public class TestClass {
    public static class DoSomething {
        private Call callee;
        public void execute() {
            callee.call();
        }
    }
    public static class Call {
        public void call() {
            System.out.println("real");
        }
    }
    @Tested DoSomething doSomething;
    @Injectable Call call;
    // nothing happens
    @Test
    public void testRealCall() {
        doSomething.execute();
    }
    // invocation doesn't help either
    @Test
    public void testRealCallSecondTry() {
        new MockUp<Call>() {
            @Mock
            @SuppressWarnings("unused")
            public void call(Invocation inv) {
                inv.proceed();
            }
        };
        doSomething.execute();
    }
    // this works, but requires redundant methods
    @Test
    public void testRealCallThirdTry() {
        new MockUp<Call>() {
            @Mock
            @SuppressWarnings("unused")
            public void call() {
                System.out.println("real");
            }
        };
        doSomething.execute();
    }
    @Test
    public void testFakeCall() {
        new MockUp<Call>() {
            @Mock
            @SuppressWarnings("unused")
            public void call() {
                System.out.println("fake");
            }
        };
        doSomething.execute();
    }
}

在这里,DoSomething包装Call实例,它提供了一种打印消息的方法。四个测试用例的理想输出是:

real
real
real
fake

然而,实际情况是只有 3 和 4 有效,打印:

real
fake

这将显示是否使用 @Injectable 创建实例。如果不将旧方法主体复制并粘贴到模拟版本中,则无法直接调用原始方法。这似乎真的很尴尬。有解决方法吗?

我的理解是,如果你使用@Injectable你只会得到一个空的模拟,然后你不能再调用原始方法。

我将使用的解决方法是像这样"手动"进行注入:

public class TestClass {
  public static class DoSomething {
    private Call callee;
    public void execute() {
      callee.call();
    }
  }
  public static class Call {
    public void call() {
      System.out.println("real");
    }
  }
  @Tested DoSomething doSomething;
  //@Injectable Call call;
  // nothing happens
  @Test
  public void testRealCall() {
    Deencapsulation.setField(doSomething, "callee", new Call());
    doSomething.execute();
  }
  // invocation doesn't help either
  @Test
  public void testRealCallSecondTry() {
    new MockUp<Call>() {
      @Mock
      @SuppressWarnings("unused")
      public void call(Invocation inv) {
        inv.proceed();
      }
    };
    Deencapsulation.setField(doSomething, "callee", new Call());
    doSomething.execute();
  }
  // this works, but requires redundant methods
  @Test
  public void testRealCallThirdTry() {
    new MockUp<Call>() {
      @Mock
      @SuppressWarnings("unused")
      public void call() {
        System.out.println("real");
      }
    };
    Deencapsulation.setField(doSomething, "callee", new Call());
    doSomething.execute();
  }
  @Test
  public void testFakeCall() {
    new MockUp<Call>() {
      @Mock
      @SuppressWarnings("unused")
      public void call() {
        System.out.println("fake");
      }
    };
    Deencapsulation.setField(doSomething, "callee", new Call());
    doSomething.execute();
  }
}

当我遇到同样的问题时,我遇到了这个问题。但是,现有的答案不适用于较新版本的JMockit。

如果测试类中的字段用 @Inject 注释,则测试类中需要相应的@Injectable。这意味着删除@Injectable,而是使用另一个答案中建议的MockUp模拟类是行不通的。JMockit将抱怨"缺少现场@Injectable..."。

相反,需要做的是将@Injectable注释更改为@Tested注释,即更改此注释

@Injectable Call call;

@Tested Call call;

call成为一个真实的实例,JMockit 不会抱怨丢失@Injectable。如果您需要在call中模拟某些方法,则可以像往常一样使用MockUp来完成。

new MockUp<Call>() {
  @Mock
  public void someMethodToMock() {
  }
};

最新更新