我有一个类似于此的bean:
@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...
}
}
问题是在运行测试时PostConstruct
之前调用了setUp
。
答案 0 :(得分:11)
如果您想编写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);
}
}
-
public class Test {
@Test
public void test() throws Exception {
B b = mock(b);
A a = new A(b);
// write some tests for A
}
}
如果必须使用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 })
答案 1 :(得分:1)
在我正在处理的项目中遇到了这个确切的问题,这是我根据问题代码使用的解决方案:
@Autowire
在bean中,并@PostConstruct
进行测试。@Before
中进行设置。@PostConstruct
末尾显式调用@Before
。@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...
}
}
很显然,您的@PostConstruct
方法必须是幂等的,因为它将被调用两次。此外,它还假定默认的单例bean行为。
答案 2 :(得分:0)
另一种选择是自己在测试中实例化应用程序上下文,然后在刷新上下文之前注入模拟,例如:
@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
个注释并且您希望对模拟先验设置期望时,此替代方法很有用。