8

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?

fischermatte
  • 3,327
  • 4
  • 42
  • 52
  • 1
    Couldnt you use *@BeforeClass* ? Or maybe delete after every test with @After, would be more save anyway. – Hendrik Jander Jan 25 '16 at 23:09
  • 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 Jan 25 '16 at 23:40
  • with [@TestExecutionListener ](http://stackoverflow.com/questions/12404636/what-is-the-difference-between-beforeclass-and-spring-testexecutionlistener-be) you can achieve the same. – Hendrik Jander Jan 26 '16 at 01:05
  • Which versions of `spring-test` and Spring Boot are you using? – Sam Brannen Jan 28 '16 at 15:26
  • Thx, sam. I am using spring 4.2 with spring boot 1.3.2 – fischermatte Jan 28 '16 at 15:44

1 Answers1

7

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.
Raj
  • 22,346
  • 14
  • 99
  • 142
Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
  • thx for this hint. In case some one else needs to this - I followed your advises here http://stackoverflow.com/a/28141143/524906 and it worked. – fischermatte Jan 28 '16 at 20:52