I have a passing unit test, but I don't know why its working. Setting debug points in IntelliJ shows that the Autowired property is null throughout. I've published the project on GitHub in case someone wants to take a look: https://github.com/leonj1/spring_spark
Below is the code where I have commented debug points (eg #1, #2, #3). When debug point #1 + #2 get hit, the @Autowired PersonRoute personRoute
is always null. This is probably because the inner static class gets instantiated ahead of @ContextConfiguration
parsing @Configuration
.
But then how does testServer
in @ClassRule
get the rehydrated SimpleController
? I know Spring uses Reflections to inspect and set properties, but it would be news to me if it also does that for objects which are already instantiated.
I'm trying to understand this because at the moment not knowing makes me have less confidence in the test.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
@DatabaseSetup("persons-entities.xml")
@Transactional
public class SimpleControllerTest {
@Configuration
@ComponentScan(basePackages = {"com.jose.sandbox"})
static class SomeConfig {
// because @PropertySource doesn't work in annotation only land
@Bean
public PropertyPlaceholderConfigurer propConfig() {
// #3 debug point 3, then shows Spring creating beans
// but how does SimpleController.class get the property set?
// does this mean 2 different SimpleController objects exist in the JVM, but only one is "wired"?
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocation(new ClassPathResource("application.properties"));
return ppc;
}
}
@Component
public static class TestControllerTestApplication implements SparkApplication {
// #1 debug point 1 in IDE shows this as NULL
@Autowired PersonRoute personRoute;
@Override
public void init() {
new SimpleController(this.personRoute);
}
}
@ClassRule
public static SparkServer<SimpleControllerTest.TestControllerTestApplication> testServer
= new SparkServer<>(SimpleControllerTest.TestControllerTestApplication.class, 4567);
@Test
public void verifyGetAllPeople() throws Exception {
// given
String payload = null;
// when
SparkClient.UrlResponse response = testServer.getClient().doMethod("GET", "/people/count", payload);
// then
int expected = 3; // because that's how many exist in persons-entities.xml
assertEquals(200, response.status);
assertEquals(expected, Integer.parseInt(response.body));
assertNotNull(testServer.getApplication());
}
@Mock
Response response;
}
Here is the Controller which initial debug point shows NULL for the Autowired
property. The debug point never gets hit again, so how is this being set?
@Component
public class SimpleController {
// #2 debug point 2, also shows this as NULL
@Autowired PersonRoute personRoute;
public SimpleController() {}
public SimpleController(PersonRoute personRoute) {
this.personRoute = personRoute;
}
@PostConstruct
public void init() {
get("/people/count", this.personRoute);
}
}
Any help would be appreciated since it difficult to build on this test until its understood.
But then how does
testServer
in@ClassRule
get the rehydratedSimpleController
?
It doesn't.
In fact, you can comment out all of the following code in your test as follows, and the test still passes.
// @Component
public static class TestControllerTestApplication implements SparkApplication {
// #1 debug point 1 in IDE shows this as NULL
// @Autowired PersonRoute personRoute;
@Override
public void init() {
// new SimpleController(this.personRoute);
}
}
Here is the Controller which initial debug point shows NULL for the
Autowired
property. The debug point never gets hit again, so how is this being set?
Spring injects the @Autowired PersonRoute
into the SimpleController
using reflection.
Basically, what's happening is the following.
com.jose.sandbox
base package which results in fully initialized PersonRepository
, PersonRoute
, and SimpleController
beans. PersonRoute
bean gets injected into the SimpleController
bean. init()
method of the SimpleController
bean gets invoked by Spring since you annotated it with @PostConstruct
, and that registers the route with Spark via the static get(...)
method invocation. TestControllerTestApplication
during the "before class" phase for the JUnit 4 Rule, but the route has already been registered. Thus there is no need for your TestControllerTestApplication
to actually do anything. That's why I commented out the code above. In other words, you only need the empty TestControllerTestApplication
implementation because the SparkServer
from the Spark Test framework requires it. Without Spring it would make sense to implement that, but due to the way you have configured things with Spring there is no need to implement it.
Hope this clarifies things for you.
Regards,
Sam ( author of the Spring TestContext Framework )
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.