用post构造测试springbean
我有一个类似的bean:用post构造测试springbean,spring,junit,postconstruct,Spring,Junit,Postconstruct,我有一个类似的bean: @Service public class A { @Autowired private B b; @PostConstruct public void setup() { b.call(param); } } @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = { Application.class
@Service
public class A {
@Autowired
private B b;
@PostConstruct
public void setup() {
b.call(param);
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { Application.class, Config.class })
@WebIntegrationTest(randomPort = true)
public class Test {
@Autowired
B b;
@Before
public void setUp() throws Exception {
when(b.call(any())).thenReturn("smth");
}
@Test
public void test() throws Exception {
// test...
}
}
问题是运行测试时,在
setUp
之前调用PostConstruct
如果要编写a
的单元测试,则不要使用Spring。相反,自己实例化A
并传递B
的存根/模拟(通过使用构造函数注入或ReflectionTestUtils
设置私有字段)
例如:
@Service
public class A {
private final B b;
@Autowired
public A(B b) {
this.b = b;
}
@PostConstruct
public void setup() {
b.call(param);
}
}
-
如果您必须使用Spring,因为您想编写一个集成测试,请使用不同的应用程序上下文,在该上下文中用存根/模拟替换B
例如,假设B
在Production
类中实例化,如下所示:
@Configuration
public class Production {
@Bean
public B b() {
return new B();
}
}
为测试编写另一个@Configuration
类:
@Configuration
public class Tests {
@Bean
public B b() {
// using Mockito is just an example
B b = Mockito.mock(B.class);
Mockito.when(b).thenReturn("smth");
return b;
}
}
使用@SpringApplicationConfiguration
注释在测试中引用它:
@SpringApplicationConfiguration(classes = { Application.class, Tests.class })
另一种选择是自己在测试上实例化应用程序上下文,然后在刷新上下文之前注入模拟,例如,如:
@Configuration
@ComponentScan
public class TestConfiguration {}
...
ClassToMock mock = mock(ClassToMock.class);
AnnotationConfigApplicationContext c = new AnnotationConfigApplicationContext();
c.getDefaultListableBeanFactory().registerResolvableDependency(
ClassToMock.class,
mock);
c.register(TestConfiguration.class);
c.refresh();
当上下文中有
@PostConstruct
注释,并且您希望在模拟之前设置期望值时,此备选方案非常有用。我正在处理的一个项目正好遇到了这个问题,以下是我在问题代码方面使用的解决方案:
@Autowire
,将@PostConstruct
添加到测试中@中进行设置
@PostConstruct
之前的@末尾的@PostConstruct
显然,您的
@PostConstruct
方法必须是幂等的,因为它将被调用两次。它还假设默认的单例bean行为。@hzpz类A有其他逻辑,这些逻辑在后面的测试中调用。回答你们的问题,我想测试一下A班的逻辑。谢谢!是的,几乎是对的。另外,@Configuration公共类测试{@Bean public B B(){B B B=Mockito.mock(B.class);Mockito.when(B).thenReturn(“smth”);return B;}
解决方案的第一部分是通过终止测试的“集成”部分来避免真正的问题。第二部分很重要better@cahen这就是为什么我从“如果你想写一个单元测试”开始。根据我的经验,人们通常倾向于在实际需要单元测试时编写集成测试。@Andy我根据您的评论更新了我的答案。在解决方案的第一部分中,您避免了问题,在解决方案的第二部分中,您也通过不使用@Service annotation来避免问题
@Configuration
@ComponentScan
public class TestConfiguration {}
...
ClassToMock mock = mock(ClassToMock.class);
AnnotationConfigApplicationContext c = new AnnotationConfigApplicationContext();
c.getDefaultListableBeanFactory().registerResolvableDependency(
ClassToMock.class,
mock);
c.register(TestConfiguration.class);
c.refresh();
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { Application.class, Config.class })
@WebIntegrationTest(randomPort = true)
public class Test {
// wire in the dependency as well
@Autowired
A a;
@Autowired
B b;
@Before
public void setUp() throws Exception {
when(b.call(any())).thenReturn("smth");
// "manual" call to @PostConstruct which will now work as expected
a.setup();
}
@Test
public void test() throws Exception {
// test...
}
}