简体   繁体   English

@PostConstruct中的JMockit模拟私有方法

[英]JMockit mock private method in @PostConstruct

Context 上下文

In my class under test there is a @PostConstruct annotated method that invokes another private method. 在我的测试类中,有一个@PostConstruct注释方法,该方法调用另一个私有方法。

@PostConstruct
public void init() {
    longWork(); // private method
}

JMockit's default behaviour is to execute @PostConstruct methods of @Tested classes upon injection. JMockit的默认行为是在注入时执行@Tested类的@PostConstruct方法。

If a @Tested class has a method annotated with javax.annotations.PostConstruct, it should be executed right after injection. 如果@Tested类的方法带有javax.annotations.PostConstruct注释,则应在注入后立即执行。

https://github.com/jmockit/jmockit1/issues/13 https://github.com/jmockit/jmockit1/issues/13

Problem 问题

JMockit calls the init() method, what I don't want. JMockit调用了我不想要的init()方法。

From the thread dump: 从线程转储:

at com.me.Worker.longWork(Worker.java:56)
at com.me.Worker.longWork.init(Worker.java:47)
...
at mockit.internal.util.MethodReflection.invoke(MethodReflection.java:96)

How can that call be mocked/removed/blocked? 该呼叫如何被模拟/删除/阻止?

Attempts 尝试

I tried to mock the init and longWork methods as shown below. 我试图模拟initlongWork方法,如下所示。 However that results in NullPointerException as sut is not injected yet. 但是,由于尚未注入sut ,因此会导致NullPointerException

@Before
public void recordExpectationsForPostConstruct()
{
    new NonStrictExpectations(sut) {{ invoke(sut, "init"); }};
}

You can try manually initializing your class to be tested without using @Tested. 您可以尝试手动初始化要测试的类,而无需使用@Tested。 And then, set mock dependencies via setter methods (or using mockit.Deencapsulation.setField() method). 然后,通过setter方法(或使用mockit.Deencapsulation.setField()方法)设置模拟依赖项。

You can try something similar to this; 您可以尝试类似的操作;

//Define your class under test without any annotation
private MyServiceImpl serviceToBeTested;

//Create mock dependencies here using @Mocked like below
@Mocked Dependency mockInstance;



@Before
public void setup() {
    //Initialize your class under test here (or you can do it while declaring it also). 
    serviceToBeTested = new MyServiceImpl();

    //Set dependencies via setter (or using mockit.Deencapsulation.setField() method)
    serviceToBeTested.setMyDependency(mockInstance);

    //Optionally add your expectations on mock instances which are common for all tests
    new Expectations() {{
        mockInstance.getName(); result = "Test Name";
        ...
    }};
}

@Test
public void testMyMethod(@Mocked AnotherDependency anotherMock) {
    //Add your expectations on mock instances specifics to this test method.
    new Expectations() {{
        mockInstance.getStatus(); result = "OK";
        anotherMock.longWork(); result = true;
        ...
    }};

    //Set dependencies via setter or using mockit.Deencapsulation.setField() method
    Deencapsulation.setField(serviceToBeTested, "myAnotherDep", anotherMock);

    //Invoke the desired method to be tested
    serviceToBeTested.myMethod();

    //Do the verifications & assertions
    new Verifications() {{
      ....
      ....

    }};

}

Perhaps you could delegate the longWork method to a different class and mock this one. 也许您可以将longWork方法委托给另一个类并模拟该类。 Difficulties while writing tests is often a sign of design flaws 编写测试时遇到困难通常是设计缺陷的标志

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM