Spring boot unit test unable to share testcontainer between 2 test classes - spring

I have a postgres test container class:
#Testcontainers
open class ContainerTest {
companion object {
#Container
var postgresDBContainer = PostgreSQLContainer<Nothing>("postgres:13.4-alpine").apply {
withExposedPorts(...)
withDatabaseName(...)
withUsername(...)
withPassword(...)
}
#DynamicPropertySource
#JvmStatic
fun dbProperties(registry: DynamicPropertyRegistry) {
registry.add("spring.datasource.url", postgresDBContainer::getJdbcUrl)
registry.add("spring.datasource.password", postgresDBContainer::getPassword)
registry.add("spring.datasource.username", postgresDBContainer::getUsername)
}
}
}
And I have 2 classes which extend this class (jupiter tests):
#SpringBootTest
#ActiveProfiles("test")
class TestClass1(
#Autowired val service: SomeService
) : ContainerTest() {
#Test
fun `should return`() {
...
}
}
And test class 2:
#SpringBootTest
#ActiveProfiles("test")
class TestClass2(
#Autowired val service: SomeService2
) : ContainerTest() {
#Test
fun `should return`() {
...
}
}
If I uncomment one of the classes, the build passes successfully, but when I build the project with both test classes, I am getting the following exception:
org.springframework.jdbc.CannotGetJdbcConnectionException at TestClass1.kt:23
Caused by: java.sql.SQLTransientConnectionException at TestClass1.kt:23
Caused by: org.postgresql.util.PSQLException at ConnectionFactoryImpl.java:319
Caused by: java.net.ConnectException at PlainSocketImpl.java:-2
It seems that after all the tests of TestClass1 pass, the testcontainer is stopped, am I missing something?
How can I share the same testcontainer between test classes?
Thanks!

Indeed, the #Testcontainers annotation will call start/stop on a per-method or per-class basis on annotated container instances.
I would recommend to use Manual Lifecycle Control instead of the JUnit5 extension for controlling the container lifecycle.

Related

Spring boot test doesn't Autowire all dependencies

I have a bit of a confusing error in my test scenario.
We want to refactor an Application that is not tested at all. To ensure that we have the same outcame after refactoring I'll write some integration tests for one Controller class.
#RestController
#RequestMapping("/rfq")
public class RfqController {
#Autowired
private RfqRepository rfqRepo;
#Autowired
private RfqDao rfqDao;
...
#PostMapping("/get")
public #ResponseBody BuyerRfqView getRFQ(#RequestBody SingleIdBody body) {
int id = body.getId();
Optional<Rfq> rfq = rfqRepo.getById(id);
...
}
}
In that case I want to test with testcontainers and spring-boot-test everything worked well, containers are up and running and the application starts so far. But the problem is that at runtime the spring-boot-test doesn't Autowire rfqRepo in the class under test. In the Testclass, every single dependency is in the ComponentScan or EntityScan and the repositories are also injected. I have no clue why this is not working. when the test is running I get a Nullpointer Exception by rfqRepo ...
here is the Test class:
#SpringBootTest(classes = RfqController.class, webEnvironment =
SpringBootTest.WebEnvironment.RANDOM_PORT)
#ComponentScan({...})
#EnableJpaRepositories({...})
#EntityScan({...})
#EnableAutoConfiguration
#ActiveProfiles("local")
#Testcontainers
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class RfqControllerTest {
#Container
private static OracleContainer database = new OracleContainer(
"oracleinanutshell/oracle-xe-11g:latest")
.withExposedPorts(1521, 5500)
.withPassword("...");
#InjectMocks
RfqController rfqController;
#DynamicPropertySource
static void databaseProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", database::getJdbcUrl);
registry.add("spring.datasource.username", database::getUsername);
registry.add("spring.datasource.password", database::getPassword);
}
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
ScriptUtils.runInitScript(new JdbcDatabaseDelegate(database, ""), "ddl.sql");
}
#Test
void testGetRFQ() {
BuyerRfqView result = rfqController.getRFQ(new SingleIdBody(176501));
Assertions.assertEquals(new BuyerRfqView(), result);
}
}
In the SpringBootTest annotation you are only using RfqController. That's the only class then that is available during test.
#SpringBootTest(classes = RfqController.class, webEnvironment =SpringBootTest.WebEnvironment.RANDOM_PORT)
So you have to add all classes that are needed for your tests.

Spring Junit Exception -- java.lang.IllegalStateException: Found multiple #SpringBootConfiguration annotated classes

I have a SpringBoot Project and it has two classes annotated with #SpringBootApplication.
I have written a junit test like this
#RunWith(SpringRunner.class)
#WebMvcTest(value = TestController.class)
public class Test1 {
#Test
public void test1(){
}
}
When i run this test am getting exception
java.lang.IllegalStateException: Found multiple #SpringBootConfiguration annotated classes.
I want the test to load only the controller and not the complete context.
Any help on this?
Try to add #ContextConfiguration annotation to your test class.
#RunWith(SpringRunner.class)
#ContextConfiguration(classes=Application.class)
#WebMvcTest(value = TestController.class)
public class Test1 {
#Test
public void test1(){
}
}

How to inject a service component in Spring Test class using Kotlin?

I am trying to test the business logic/services. Using Kotlin in my Spring Boot application, I am trying to instantiate my services, using the test class constructor. But this is not allowed, as I am getting a ParameterResolutionException.
This is my code:
#SpringBootTest
class IntermediateApplicationTests(val ticketService: TicketService) {
#Test
fun contextLoads() {
}
}
How can I achieve this?
Thanks for every help!
You can use the #Autowired annotation to do this:
#SpringBootTest
class IntermediateApplicationTests #Autowired constructor(
val ticketService: TicketService
) {
#Test
fun contextLoads() {
}
}
In fact this is the recommended way of doing injection and the IDEA Spring plugin will complain if you do it some other way.

Spring boot #Import for loading config integration test not working

I have a simple spring boot app and i am implementing some integration test. I have 2 classes one that will hold my common configuration (Demo3ApplicationTests ) and the other one my integration test class(DumyClassTest), please find below it is empty for the time being:
#SpringBootTest(classes = Demo3Application.class)
class Demo3ApplicationTests {
#Test
void contextLoads() {
}
}
My integration test class:
#Import(value = Demo3ApplicationTests.class)
public class DumyClassTest{
#Autowired
DemoService demoService;
#Test
public void testImportConfig() {
demoService.logDummyMsg();
}
}
When I run the test testImportConfig the demoService value is null as I guess the #import i am not setting it up correctly. However when I extends that is DumyClassTest extends Demo3ApplicationTests the demoService is not null and the test is run correctly.
Any idea why when I use the import annotation the demoService is null?
Thanks in advance.

Test Spring Data Repository in a project without a Spring Boot Application main class

I have a small project that does not contain a class to run a Spring Boot Application. In that class I only have some configuration and some repositories. I would like to test those repositories inside the small project.
For that, I have the following:
#SpringBootTest
#DataJpaTest
public class TaskRepositoryTest extends AbstractTestNGSpringContextTests {
#Autowired
private TaskRepository taskRepository;
#Test
public void test() {
taskRepository.save(new Task("open"));
}
}
But I get the following error
Caused by: java.lang.NoSuchMethodError: org.springframework.boot.jdbc.DataSourceBuilder.findType(Ljava/lang/ClassLoader;)Ljava/lang/Class;
Any idea what I have to do?
This works for me with Spring Boot 2.0.3, H2 and latest RELEASE testng:
#EnableAutoConfiguration
#ContextConfiguration(classes = TaskRepository.class)
#DataJpaTest
public class TaskTest extends AbstractTestNGSpringContextTests {
#Autowired
private TaskRepository taskRepository;
#Test
public void test() {
taskRepository.save(new Task("open"));
}
}
LE:
In the previous version of the answer I've used #SpringBootTest #DataJpaTest but that seems to be the wrong way to do it.
The following message would appear:
[main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither #ContextConfiguration nor #ContextHierarchy found for test class [one.adf.springwithoutapp.TaskTest], using SpringBootContextLoader
Using #ContextConfiguration with #DataJpaTest removes that warning and IntelliJ doesn't mark the mocked taskRepository as Could not autowire. No beans of 'TaskRepository' type found.

Resources