单元测试时如何跳过@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);
}
}
我有一个计划任务,每天晚上汇总数据。每当我启动应用程序时任务 运行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);
}
}