Why container does not start automatically in Test? - spring-boot

I have the following class
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
#RunWith(SpringRunner.class)
#Testcontainers
#ContextConfiguration(initializers = OperationCodeRepositoryTest.DockerDatasourceInitializer.class)
public class OperationCodeRepositoryTest {
#Container
private static MSSQLServerContainer<?> container = new MSSQLServerContainer<>(
"mcr.microsoft.com/mssql/server:2019-latest").acceptLicense()
.withInitScript("database/init.sql");
#Autowired
private OperationCodeRepository repository;
public static class DockerDatasourceInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext, "spring.datasource.url=" + container.getJdbcUrl(),
"spring.datasource.username=" + container.getUsername(),
"spring.datasource.password=" + container.getPassword());
}
}
}
// SOme tests hre
Whenever I try to run my tests,I get the following error :
java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
I thought that having the #Container would be enough to start the container.
The only option for me is to manually start the container by calling the start method in #BeforeClass
Ex :
#BeforeClass
public static void init() {
try{
container.start();
} catch(Exception e){
e.printStackTrace();
}
}
What am I doing wrong ?
THanks

Related

Force Spring Boot to override default configurations

I'm trying to implement the usage of Testcontainers as an alternative for IT test on a project, currently the project it's using H2 as datasource for testing, the matter is that no matter what I try I'm not able to override those configurations in order to create a custom datasource by using a DB container, what I've tried so far:
Setting the datasource attributes directly on the application-testcontainers.yml file + #ActiveProfiles("testcontainers") annotation.
spring:
datasource:
url: jdbc:postgresql://localhost:5432/testcontainers
username: user
password: 123456
#RunWith(MockitoJUnitRunner.class)
#ActiveProfiles("testcontainers")
public final class MyTestClass {
}
Creating an extension class in order to set the datasource attributes (overriding the beforeAll method):
public class TestcontainersExtension implements BeforeAllCallback, AfterAllCallback {
private PostgreSQLContainer<?> container;
#Override
public void beforeAll(ExtensionContext context) throws Exception {
container = new PostgreSQLContainer<>("postgres:9.4")
.withDatabaseName("testcontainers")
.withUsername("user")
.withPassword("123456")
.withExposedPorts(5432);
container.start();
System.setProperty("spring.datasource.url", container.getJdbcUrl());
System.setProperty("spring.datasource.username", container.getUsername());
System.setProperty("spring.datasource.password", container.getPassword());
}
#Override
public void afterAll(ExtensionContext context) throws Exception {
}
}
#RunWith(MockitoJUnitRunner.class)
#ExtendWith(TestcontainersExtension.class)
public final class MyTestClass {
}
Using #DynamicPropertySource method in order to set the configuration properties dynamically
#RunWith(MockitoJUnitRunner.class)
MyTestClass {
#Container
static PostgreSQLContainer<?> container =
new PostgreSQLContainer<>("postgres:9.4")
.withDatabaseName("testcontainers")
.withUsername("user")
.withPassword("123456")
.withExposedPorts(5432);
#DynamicPropertySource
static void setTestProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", () -> container.getJdbcUrl());
registry.add("spring.datasource.username", () -> container.getUsername());
registry.add("spring.datasource.password", () -> container.getPassword());
}
}
#ContextConfiguration + an initializer class, also trying to set the properties dynamically
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(initializers = {MyTestClass.Initializer.class})
public final class MyTestClass {
#Container
static PostgreSQLContainer<?> container =
new PostgreSQLContainer<>("postgres:9.4")
.withDatabaseName("testcontainers")
.withUsername("user")
.withPassword("123456")
.withExposedPorts(5432);
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertyValues.of(
"spring.datasource.url=" + container.getJdbcUrl(),
"spring.datasource.username=" + container.getUsername(),
"spring.datasource.password=" + container.getPassword()
).applyTo(applicationContext.getEnvironment());
}
}
}
None of the above worked for me, I’ll appreciate any hint or alternative.

Spring JUnit 5 ExtendWith TestContainer

I'm trying to reutilize a container and I'm trying to use JUnit5 ExtendWith feature but I'm still getting:
Connection to localhost:5432 refused.
If I have the same logic inside each test everything works as expected.
#Testcontainers
#SpringBootTest
#ExtendWith({PostgresTestContainersExtension.class})
public class ApplicationJUnit5Test {
#Autowired
private HeroClassicJDBCRepository repositoryUnderTest;
#Test
public void test1() {
System.out.println("junit version: " + Version.id());
Collection<Hero> heroes = repositoryUnderTest.allHeros();
assertThat(heroes).hasSize(1);
repositoryUnderTest.addHero(new Hero("bb", "bb"));
Collection<Hero> heroesAfter = repositoryUnderTest.allHeros();
assertThat(heroesAfter).hasSize(2);
}
}
Extention:
public class PostgresTestContainersExtension implements BeforeAllCallback,
BeforeTestExecutionCallback {
private static final String IMAGE_NAME = "registry.mycomp.com/db/mariadb:10.4.11";
#DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", container::getJdbcUrl);
registry.add("spring.datasource.password", container::getPassword);
registry.add("spring.datasource.username", container::getUsername);
}
#Container
public static PostgreSQLContainer container = new PostgreSQLContainer()
.withUsername("duke")
.withPassword("password")
.withDatabaseName("test");
#Override
public void beforeAll(ExtensionContext extensionContext) {
startContainerIfNeed();
}
#Override
public void beforeTestExecution(ExtensionContext extensionContext) {
startContainerIfNeed();
}
public void startContainerIfNeed() {
if (!container.isRunning()) {
container.start();
}
}
}
As far as I know #DynamicPropertySource can only be used in the test class itself or a superclass. You’ll have to move the properties method over.

How to use Spring boot AutoWired and ScheduledExecutorService?

I need to use autowired in more than one class with ScheduledExecutorService, what I have tried is shown in this code. logging size of User list in below example always shows 0, even after user added to arraylist. How to properly use Autowired and ScheduledExecutorService in spring boot?
#Component
public class AnotherClass {
List<User> users = new ArrayList();
public void addUser(User user){
users.add(user);
}
public void logUsers(){
logger.info("User size " + users.size()); <================= Always logs 0, when called from executor
}
}
#RestController
public class SecondClass {
#Autowired
private AnotherClass anotherClass;
#GetMapping(value="/user/test")
public void logUsers(){
anotherClass.addUser(new User());
}
}
Application Class
#Component
#SpringBootApplication
public class SpringBootDemoApplication {
private ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
#Autowired
private AnotherClass anotherClass;
#PostConstruct
public void init() {
logger();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
public void logger(){
exec.scheduleAtFixedRate(new Runnable(){
#Override
public void run(){
try {
anotherClass.logUsers();
}catch (Exception e){
}
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
}
}
The code works if you use the Spring #Autowired and not the #AutoWired Annotation.

Unable to mock a method with Mockito and Spring

I have a class JobDelegate for which i am writing unit tests using mockito. I am unable to mock the HTTPOperations class. I have tried using setter injection from test class as well. But it does not work. Below the latest revision of the code. I tried using Power mock. but none of them was helpful. I am unable to predict which is going wrong.
Unit Test code
#ContextConfiguration(locations= "file:src/main/webapp/WEB-INF/spring-
context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
//#RunWith(PowerMockRunner.class)
/#PowerMockIgnore({ "javax.xml.*", "org.xml.*", "org.w3c.*" })
//#PrepareForTest({ HTTPOperations.class})
public class JobSubmissionDelegateTest{
private static Logger LOGGER = null;
private JobDelegate jobDelegate ;
private JobManager jobImpl;
#InjectMocks
private HTTPOperations operations;
//#Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
#Before
public void setupTests() {
jobDelegate = new JobDelegate();
jobManager = new DBJobManagerImpl();
operations = new HTTPOperations();
jobManager.setHttpOperations(operations);
jobSubmissionDelegate.setJobImpl(jobManager);
//HTTPOperations httpOperationsSpy =spy(HTTPOperations.class);
//doReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}").when(myClassSpy).method1();
MockitoAnnotations.initMocks(this);
}
#Test
public void testExecuteJob() throws IOException {
// PowerMockito.mockStatic(HTTPOperations.class);
Mockito.when(operations.submitHttpPostRequest(any(), anyString())).thenReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}");
//System.out.println("==>"+operations.submitHttpPostRequest(null, ""));
...........
int runID = jobDelegate.executeJob(jobDetails);
System.out.println("Run ID here " + runID);
}
}
public class JobDelegate {
// This is an interface.. and the implementation is passed from spring-
context.xml
#Autowired
private JobManager jobImpl;
public int executeJob(JobDTO jobDto) {
............
return jobImpl.runBatchJob(jobDto);
}
}
public class DBJobManagerImpl implements JobManager{
#Autowired
private URIUtils uriUtils;
#Autowired
private HTTPOperations httpOperations;
#Override
public int runBatchJob(JobDTO jobDto) throws Exception {
UriComponentsBuilder uri = uriUtils.createURI(ConfigUtil.getUrI());
String response = httpOperations.submitHttpPostRequest(uri, runSubmitJson);
System.out.println("Response ==> " +response);
.................
}
}
I was able to resolve the issue using PowerMock.
Below the code
#RunWith(PowerMockRunner.class)
#ContextConfiguration(locations= "file:src/main/webapp/WEB-
INF/Enrichment_Context.xml")
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#PowerMockIgnore({ "javax.xml.*", "org.xml.*", "org.w3c.*",
"javax.management.*" })
#PrepareForTest({ HTTPOperations.class})
public class JobDelegateTest {
#Autowired
private JobDelegate jobSubmissionDelegate;
#Test
public void testExecuteJob() throws IOException {
PowerMockito.mockStatic(HTTPOperations.class);
PowerMockito.when(HTTPOperations.submitHttpPostRequest(Mockito.any(),
Mockito.anyString())).thenReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}");
...................
int runID = jobSubmissionDelegate.executeJobSubmission(jobDetails);
}
}

Spring boot repository.save() does not work in test class

I have this test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { CrimeServiceDBImpl.class, CrimeController.class, Crime.class })
#ComponentScan("com.springmiddleware")
#EntityScan(basePackages = {"com.springmiddleware.entities"})
#DataJpaTest
#AutoConfigureTestDatabase(replace = Replace.NONE)
#EnableJpaRepositories("com.springmiddleware")
public class TestCrimeServiceDB {
#Autowired
private CrimeServiceDBImpl service = new CrimeServiceDBImpl();
#Test
public void getAll() {
try {
List<Crime> list = this.service.getAllCrimes();
assertTrue(list.size()!=0);
} catch (IOException e) {
e.printStackTrace();
}
}
}
The method getAllCrimes() from the service class does just this:
#Service
public class CrimeServiceDBImpl implements CrimeService{
#Autowired
private CrimeRepository repository;
private List<Crime> list = new ArrayList<Crime>();
public CrimeServiceDBImpl() {
list = UtilityMethods.readFromCSV();
};
#Override
public List<Crime> getAllCrimes() throws IOException {
repository.saveAll(list);
return this.repository.findAll();
}
If I call this method when running the application, it correctly add all my objects to the database, but when it's called from the test it doesn't add anything, but no exception is thrown.
Which database are you using? Do you mean the data is not persisted in the database after the test has finished? That's because a test always perform a rollback/cleanup when it has finished its work.

Resources