6
votes

I am looking for the recommended approach for populating test data programmatically in integration tests using spring / spring boot. I am using HSQLDB (inmemory).

There is the possibility to execute SQL scripts in spring for integration tests like this:

@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
public void userTest {
    // execute code that relies on the test schema and test data
}

Instead of writing SQL scripts I would like to insert data for multiple test methods in one integration test programmatically like this:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BookstoreApp.class)
@IntegrationTest
public class BookRepositoryTest {
    @Autowired
    private BookRepository bookRepository;

    @Before // not working
    public void setUp() throws Exception {
        bookRepository.save(new Book("Donald Duck 1", "Walt Disney", "0-14-020652-3"));
        bookRepository.save(new Book("Donald Duck 2", "Walt Disney", "0-14-020652-4"));
        bookRepository.save(new Book("Micky Mouse", "Walt Disney", "0-14-020652-5"));
    }

    @Test
    public void findByTitle() {
        List<Book> books = bookRepository.findByTitle("Duck");
        Assert.assertEquals(2, books.size());
    }

    @Test
    public void getByIsbn() {
        Book book = bookRepository.getByIsbn("0-14-020652-4");
        Assert.assertEquals("0-14-020652-4", book.getIsbn());
        Assert.assertEquals("Donald Duck 2", book.getTitle());
    }

}

Each Test of this example runs just fine when being executed separately. But the second one (getByIsbn) will fail, when running them together. So obviously @Before is the wrong annotation to use here, since the books will be inserted twice.

How can I enforce the database setup being executed only once?

1
Couldnt you use @BeforeClass ? Or maybe delete after every test with @After, would be more save anyway.Hendrik Jander
thx. @BeforeClass is not working since spring context won't be up at that time, but @After would be possible. Not the nicest approach I think, since you need to keep track of everything you inserted.fischermatte
with [@TestExecutionListener ](stackoverflow.com/questions/12404636/…) you can achieve the same.Hendrik Jander
Which versions of spring-test and Spring Boot are you using?Sam Brannen
Thx, sam. I am using spring 4.2 with spring boot 1.3.2fischermatte

1 Answers

7
votes

Replacing @IntegrationTest with @Transactional (at the class level) should likely solve your problem.

Reasoning:

  1. @IntegrationTest launches your entire Spring Boot application, but this appears to be overkill for your scenario.
  2. @Transactional will cause your tests to execute within a test-managed transaction that will be rolled back after the test completes; code executed within the @Before method will be executed inside the test-managed transaction.