I have a SpringBoot application, basically with a structure similar to the following:
application:
@SpringBootApplication
public class MyApplication {
@Autowired
MainService mainService;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
@KafkaListener(topics = "myTopic")
public void listen(String message) {
this.graphicService.performWork();
}
}
first service:
@Service
public MainService {
@Autowired MyService myService;
public performWork() {
this.myService.doStuff();
}
}
second service:
@Service
public class MyService {
// server.param1 and server.param2 are defined in application.properties file
@Value("${server.param1}")
private String param1;
@Value("${server.param2}")
private String param2;
@PostConstruct
public void initService(){
}
public void doStuff() {
// do stuff assuming the parameters param1 and param 2 of this autowired service have already been initialized
}
}
I have a junit like the following:
@SpringBootTest(classes = MyApplication.class)
class MyServiceTest {
@Test
void testMyService() {
MyService myService = new MyService();
myService.doStuff();
}
}
When I execute testMyService, I get an exception thrown, essentially like this:
java.lang.IllegalStateException: Failed to load ApplicationContext
.
.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myApplication': Unsatisfied dependency expressed through field 'mainService';
.
.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainService': Unsatisfied dependency expressed through field 'myService'
.
.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myService': Injection of autowired dependencies failed
.
.
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'server.param1' in value "${server.param1}"
The application works fine operationally. I thought that the way I set up the junit, the springboot app would simply fire up and the parameters found in the application.properties file would simply be available to MyService service as are they when I run the application itself (not the junit).
Obviously I am doing something wrong, and the application context is not available the way I have this junit set up. I would be grateful for any ideas for getting this to work properly.
Thanks!
CodePudding user response:
Wire your class under test in the Junit test like in any production code class.
The @SpringBootTest
will autodetect the @SpringBootApplication
, so no extra parameter is needed. Just wire the needed dependencies like you would in the application classes.
The test will use the src/test/resources/application.properties
(or yml) file, if present. If not present, the src/main/resources/application.properties
is used. So if you use environment variables in your production application.yml
copy this file to the test resources and fill the parameters with dummy parameters for test.
@SpringBootTest
class MyServiceTest {
@Autowired MyService myService;
@Test
void testMyService() {
myService.doStuff();
}
}
If you like you can add the parameters in the test class with @TestPropertySource(properties
@SpringBootTest
@TestPropertySource(properties = {
"server.param1=srv1",
"server.param2=srv2"
})
class MyServiceTest {
...
Make sure you have spring-boot-starter-test
in your dependencies.
Maven Example:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.5</version>
<scope>test</scope>
</dependency>