Reputation: 888
I'm using Spring Boot in a little PoC, and I'm trying to test a @Bean implementation. I have this code:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
CommandLineRunner init(@Value("${db.resetAndLoadOnStartup:true}") boolean resetAndLoadOnStartup,
SequenceIdRepository sequenceRepository,
UserAccountRepository userAccountRepository,
BookRepository bookRepository) {
return args -> {
log.info("Init Application...");
if (resetAndLoadOnStartup) {
fillDBData(sequenceRepository, userAccountRepository, bookRepository);
}
log.info("Aplication initiated!");
};
}
private void fillDBData(SequenceIdRepository sequenceRepository,
UserAccountRepository userAccountRepository,
BookRepository bookRepository) {
// Some code...
}
...
}
How can I unit test this @Bean commandLineRunner? Yeah, maybe I could unit test the 'fillDBData' method (putting protected or with powermock), but I would like to learn if there's a way to test the Spring @Bean "completely".
Upvotes: 8
Views: 18838
Reputation: 38777
In general, to test beans you simply inject them into a Spring test. For example:
@SpringBootTest
public class FullIntegrationTest {
@Autowired CommandLineRunner runner;
// ...
@SpringJUnitConfig(TestConfig.class)
public class SingleUnitTest {
@Autowired CommandLineRunner runner;
However, the problem you have is that the runner will be executed on context startup, before any tests are run. The only way to prevent that is to not have the runner as a bean, but then it cannot be injected. It's neater if you split things up:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
@Profile("!test")
CommandLineRunner init(DbService dbService) {
return dbService::init;
}
}
@Service
public class DbService {
@Value("${db.resetAndLoadOnStartup:true}") boolean resetAndLoadOnStartup;
public void init(String... args) {
log.info("Init Application...");
// ...
}
private void fillDBData(
// ...
@SpringBootTest
@ActiveProfiles("test")
public class FullIntegrationTest {
@Autowired DbService runner;
// ...
You might like to add an interface and/or a @ConfigurationProperties
to separate things further and make future additions and testing easier.
Upvotes: 0
Reputation: 602
Create MyApplication class
package com.company.project;
//imports
@SpringBootApplication(scanBasePackages = "com.company.project")
public class FileToDbApplication {
public static void main(String[] args) {
SpringApplication.run(FileToDbApplication.class, args);
}
}
Create MyCommandLineRunner class
package com.company.project;
//imports
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Autowired
private SurValueService surValueService;
@Override
public void run(String... args) {
System.out.println("App started with " + args.length + " parameters.");
}
}
Create MyConfiguration class (you may not need @EntityScan and @EnableJpaRepositories)
package com.company.project;
//imports
@SpringBootConfiguration
@ComponentScan("com.company.project")
@EnableAutoConfiguration
@EntityScan(basePackages = "com.company.project.model")
@EnableJpaRepositories(basePackages = "com.company.project.service")
public class MyConfiguration {
}
Create MyCommandLineRunnerTest in src.test.java
package com.company.project;
//imports
@ExtendWith(SpringExtension.class)
@SpringBootTest
class MyCommandLineRunnerTest {
@Autowired
MyCommandLineRunner commandLineRunner;
@Test
public void testMain() {
commandLineRunner.run("input.txt", "input2.txt");
}
}
Upvotes: 0
Reputation: 4738
Here's how you could test with an integration test.
@RunWith(SpringJUnit4ClassRunner.class)
// Or create a test version of Application.class that stubs out services used by the CommandLineRunner
@SpringApplicationConfiguration(classes = Application.class)
public class CommandLineRunnerIntegrationTest {
@Autowired
private CommandLineRunner clr;
@Test
public void thatCommandLineRunnerDoesStuff() throws Exception {
this.clr.run();
// verify changes...
}
}
That being said, my preference would be to create a named service that implements command line runner and then unit test it with all of its dependencies mocked out. In my opinion, it's not critical to test that Spring is going to call the CommandLineRunner bean when the application loads, but that the CommandLineRunner implementation calls other services appropriately.
Upvotes: 4
Reputation: 6530
You can use OutputCapture
to see what you print in the console
@Rule
public OutputCapture outputCapture = new OutputCapture();
in your test method:
String output = this.outputCapture.toString();
assertTrue(output, output.contains("Aplication initiated!"));
Upvotes: 4