单元测试时如何跳过@PostConstruct

How to skip @PostConstruct when unit testing

我有一个计划任务,每天晚上汇总数据。每当我启动应用程序时任务 运行s,当我 运行 jUnit 测试应用程序时,我想从 运行ning 停止它。

@Scheduled(cron = "0 0 0 1 * ?")
public void SalesDataAggregation() {
    //aggregation
}

编辑

这里也调用了上面的方法

@PostConstruct
public void init(){
    SalesDataAggregation();
}

由于 @PostConstruct 注释,方法 SalesDataAggregate 在启动时为 运行。如果你想在测试期间将它从 运行 中保留下来,你可以在你的测试文件夹中创建包含 post 结构的 class 并添加 @primary注释,因此它优先于主项目中的 class。

@Primary
public class ClassContainingPostConstruct{   

}

您可以将包含 PostConstruct 的 bean 重写为 EventListener (https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2) 以在启动时触发,我认为这可能是这样做的目的.然后可以将该 bean 绑定到某些 Profile 以仅在特定启用的配置文件上触发。

另一种选择是使用 属性 有条件地触发它。

public class PostConstructBean {

public boolean isPostConstructEnabled;

public PostConstructBean(@Value("${postconstructenabled}" String value){
   isPostConstructEnabled = Boolean.parseBoolean(value);
}

@PostConstruct
public void init(){
   if(isPostConstructEnabled){
      SalesDataAggregation();
   }else{
      //NOOP
   }
}
}

然后只需将 属性 添加到您的环境 properties/overall 属性 文件中。这增加了让您更容易 loading/disabling 的 bean

的好处

在我的例子中,我的 PostConstruct 中没有任何东西会崩溃我的其他测试,只有我的 Mockito.verify,所以我决定保留 PostConstruct 使用的注入模拟 class 然后在我的测试中,重新-模拟它并使用 Mockito 和 ReflectionTestUtils 重新注入它。这避免了 bean 创建的问题,并允许我只验证新模拟的 class:

Class 正在测试中:

@Component
public class ClassUnderTest
{
    @Autowired
    private MockedClass nameOfActualBeanInClassUnderTest;

    @PostConstruct
    private void postConstructMethod()
    {
        Object param1 = new Object();
        Object param2 = new Object();
        this.nameOfActualBeanInClassUnderTest.mockedClassFunctionBeingHit(param1, param2);
    }
}

测试Class:

import static org.mockito.Mockito.*;
import org.springframework.test.util.ReflectionTestUtils;

public class Tests
{
    // Class Under Test
    @Autowired
    private ClassUnderTest classUnderTest;

    // Mocked Class
    @MockBean
    private MockedClass mockedClass;

    @Test
    public void actualTestThatAvoidsPostConstruct()
    {
        // ============== Ignore PostConstruct Errors ==============

        // Note: You will probably want to capture the current mocked class
        // to put it back in the class under test so that other tests won't fail
        MockedClass savedMockedClass = 
        (MockedClass)ReflectionTestUtils.getField(this.classUnderTest,
            "nameOfActualBeanInClassUnderTest");

        this.mockedClass = mock(MockedClass.class);
        ReflectionTestUtils.setField(this.classUnderTest,
            "nameOfActualBeanInClassUnderTest",
            this.mockedClass);

        // ============== Setup Test ==============
        Object response = new Object();

        // Set return value when mockedClass' function is hit
        // Note: Only need to pass params if your function actually has them
        when(this.mockedClass.mockedClassFunctionBeingHit(param1, param2))
            .thenReturn(response);

        // ============== Test ==============

        this.classUnderTest.testFunction();

        // ============== Verify ==============

        // Note: Only need to pass params if your function actually has them
        verify(this.mockedClass).mockedClassFunctionBeingHit(param1, param2);

        // ============== Reset Mocked Class ==============
        ReflectionTestUtils.setField(this.classUnderTest,
            "nameOfActualBeanInClassUnderTest",
            savedMockedClass);
    }
}