Data not persisted in embedded postgres DB when I use spring-boot-starter-data-jpa
Riyas-vadakkan opened this issue · 6 comments
Hi,
I configured embedded-postgres with liquibase in my application. In my integration test I call CrudRepository implementation to persist my data to embedded database. But this data is not persisted in my embedded postgres database instance, rather data is inserted into my real postgres database instance. Data is persisted in embedded postgres db is when i use native sql queries in my test method. Can somebody support to fix this?
My Test class is
package com.bosch.spring;
import com.bosch.spring.dao.Book;
import com.bosch.spring.service.impl.BookServiceImpl;
import io.zonky.test.db.postgres.embedded.LiquibasePreparer;
import io.zonky.test.db.postgres.junit.EmbeddedPostgresRules;
import io.zonky.test.db.postgres.junit.PreparedDbRule;
import liquibase.Contexts;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
@SpringBootTest
@RunWith( SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CustomTest {
@Autowired
BookServiceImpl bookService;
@Rule
public PreparedDbRule db = EmbeddedPostgresRules.preparedDatabase(LiquibasePreparer.forClasspathLocation("db/changelog/db.changelog-master.yaml", new Contexts("test")));
@Before
public void init() {
//System.out.println("init");
}
@Test
public void testTablesMade() throws Exception {
try (Connection c = db.getTestDatabase().getConnection();
Statement s = c.createStatement()) {
ResultSet rs = s.executeQuery("SELECT * FROM book");
rs.next();
assertEquals("Test Books", rs.getString(1));
}
}
@Test//test method uses spring crude repository implementation
public void testDB() throws Exception{
bookService.saveBook( new Book("Test Books"));
assertEquals("book is retrieved", "Test Books", bookService.findByName("Test Books").get(0).getName());
}
}
Hi,
it is because you use the embedded database only in the first method. The second method uses an instance of BookServiceImpl
bean, which contains a default data source provided (and configured) by Spring Boot framework. To work the library properly, it is necessary to call db.getTestDatabase()
also in the second method.
You can also consider using embedded-database-spring-test
project that is built on top of this library and is aimed to Spring Boot projects. But note that it is not yet fully optimized for use with Liquibase, so you need to make some configuration changes. Here are steps to make it work: zonkyio/embedded-database-spring-test#46
@tomix26 Thank you for your reply.
I modified the second test method as below
@test
public void testDB() throws Exception{
db.getTestDatabase();
bookService.saveBook( new BookDAO("Test Books"));
assertEquals("book is retrieved", "Test Books", bookService.findByName("Test Books").get(0).getName());
}
But still data is not inserted to embedded test database. I'm I doing it wrongly?
Also, I tried to use embedded-database-spring-test, but I couldn't configure liquibase in it. Do you have any sample applications that use embedded-database-spring-test with liquibase?
It is still the same problem. The bookService
bean still contains a default data source provided (and configured) by Spring Boot framework. You have to call db.getTestDatabase()
method and use the result of the method to change the default data source to an embedded one. Try the example bellow.
@RunWith(SpringRunner.class)
@SpringBootTest
@DirtiesContext
public class CustomTest {
@ClassRule
public static PreparedDbRule db = EmbeddedPostgresRules.preparedDatabase(LiquibasePreparer.forClasspathLocation("db/changelog/db.changelog-master.yaml", new Contexts("test")));
@TestConfiguration
static class Config {
@Bean
public DataSource dataSource() {
return db.getTestDatabase();
}
}
@Autowired
private BookServiceImpl bookService;
@Test//test method uses spring crude repository implementation
public void testDB() throws Exception{
bookService.saveBook( new Book("Test Books"));
assertEquals("book is retrieved", "Test Books", bookService.findByName("Test Books").get(0).getName());
}
}
This solution should work but is not very efficient because the lifecycle of the embedded database does not correspond to the lifecycle of the Spring framework. Therefore it is necessary to use @DirtiesContext
annotation to reset application context after each test class.
So, I recommend to use embedded-database-spring-test
library instead, which is a far better solution for Spring apps. The only thing that you need to do to make it work is add spring.flyway.enabled=false
property to your application configuration.
Hi @tomix26
Thank you for the suggestions. I used embedded-database-spring-test and it worked with liquibase.
I wanted to create a fresh DB after each test, for this I set @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) at class level. Is this a good approach?
At this moment, it is the only possible solution. In the future, I plan to improve support for Liquibase. Then it will be possible to refresh a database by @AutoConfigureEmbeddedDatabase(refreshMode = RefreshMode.AFTER_EACH_TEST_METHOD)
annotation without resetting the entire application context. For now, this feature is only supported for Flyway migration tool. Here is the related issue: zonkyio/embedded-database-spring-test#21
Wait mode ON to hear about this feature release. :)
Thank you.