问题
I have a bean similar to this:
@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...
}
}
The problem is that PostConstruct
is called before setUp
when the test is run.
回答1:
If you want to write a unit test of A
, then don't use Spring. Instead, instantiate A
yourself and pass a stub/mock of B
(either by using constructor injection or ReflectionTestUtils
to set the private field).
For example:
@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
}
}
If you have to use Spring, because you want to write an integration test, use a different application context, where you replace B
with a stub/mock.
For example, assuming B
is instantiated in a Production
class like this:
@Configuration
public class Production {
@Bean
public B b() {
return new B();
}
}
Write another @Configuration
class for your tests:
@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;
}
}
Reference it in your test with the @SpringApplicationConfiguration
annotation:
@SpringApplicationConfiguration(classes = { Application.class, Tests.class })
回答2:
another alternative is to instantiate the application context on the test yourself and then inject the mocks prior to refreshing the context, for example, as in:
@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();
This alternative is useful when there are @PostConstruct
annotations on the context and you want to set expectations on the mock prior.
来源:https://stackoverflow.com/questions/31587639/testing-spring-bean-with-post-construct