How to populate test data programmatically for integration tests in Spring? - spring

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?

Replacing #IntegrationTest with #Transactional (at the class level) should likely solve your problem.
Reasoning:
#IntegrationTest launches your entire Spring Boot application, but this appears to be overkill for your scenario.
#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.

Related

spring unit test repository save not working

I'm trying to write a unit test, for my spring server.
First it will check if a username is present or not in repository, if not so the username is available, then it will return true, and then I will save that username to my repository and check if available ot not it should return false.
Here is my code:
#Test
public void availableTest() {
String username="some_username";
LoginCredential lc=new LoginCredential();
lc.setUsername(username);
lc.setPasswordHash("1");
lc.setSessionID(0);
assertEquals(true, loginCredentialService.available(username));
loginCredentialRepository.save(lc);
assertEquals(false, loginCredentialService.available(username));
}
But for some reason, for the last assertEquals it gives me error. So I can say, the data is not being saved in repository, as I have tested my APIs using postman.
So how this can be resolved?
I think testing class is properly annotated:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MainApplicationTests {
#Autowired
private LoginCredentialService loginCredentialService;
#MockBean
private LoginCredentialRepository loginCredentialRepository;
...
You tagged the OP with H2, so I guess you know about In-Memory databases.
If you use the #SpringBootTest annotation, you are writing an integration test, so you'll test the full application as wired by Spring. For efficiency you might want to use an in-memory database for testing instead of a full SQL Server.
You can achieve this by adding H2 database as a test dependency, which will be picked up by Spring Boot for integration test repositories. Then you can inject the actual repository:
#Autowired
private LoginCredentialRepository loginCredentialRepository;
Additionaly, you can make your test #Transactional. Then every test case will run in a separate transaction, and the transaction will be rolled back after every test, so you don't need to worry about cross-test polution.
If you just want to Unit Test the LoginCredentialService, you need to stub the relevant methods on the repository, e.g.
#MockBean
private LoginCredentialRepository loginCredentialRepository;
#Test
public void availableTest1() {
when(loginCredentialRepository.existByName(username)).thenReturn(true);
assertEquals(false, loginCredentialService.available(username));
}
#Test
public void availableTest2() {
when(loginCredentialRepository.existByName(username)).thenReturn(false);
assertEquals(true, loginCredentialService.available(username));
}
You can do this as a pure Mockito test, too, without #SpringBootTest.

Spring H2 Test DB does not reset before each test

EDIT: As C. Weber suggested in the comments, the solution is to add #Transactional to the test class.
I have some tests that use an H2 in-memory DB. I need to reset the DB before each test. Although my SQL scripts are run each a test is executed, the DB is not properly reset, resulting in a missing needed entry after a delete test.
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureTestDatabase(replace=Replace.ANY, connection=EmbeddedDatabaseConnection.H2)
public class RepositoryTests {
#Autowired
private Repository repository;
#Autowired
private DataSource dataSource;
#Before
public void populateDb() {
Resource initSchema = new ClassPathResource("database/schema.sql");
Resource initData = new ClassPathResource("database/data.sql");
DatabasePopulator dbPopulator = new ResourceDatabasePopulator(initSchema, initData);
DatabasePopulatorUtils.execute(dbPopulator, dataSource);
}
#Test
public void testMethod1() {
// ...
repository.delete("testdata");
}
#Test
public void testMethod2() {
// ...
Object test = repository.get("testdata");
// is null but should be an instance
}
}
schema.sql drops all tables before recreating them. data.sql inserts all needed test data into the DB.
Running the testMethod2 alone succeeds. However, running all tests makes the test fail with a NullPointerException.
I have successfully tried to use #DirtiesContext, however this is not an option because I can't afford to have a 20 second startup for each 0.1 second test.
Is there another solution?
The Spring Test Framework provides a mechanism for the behaviour you want for your tests. Simply annotate your Test class with #Transactional to get the default rollback behaviour for each test method.
There are ways to configure the transactional behaviour of tests and also some pitfalls (like using RestTemplate inside test method), which you can read more about in the corresponding chapter of the Spring manual.
Spring Test Framework

Multiple runwith for a junit test class

Does any one know how to tackle this.
#RunWith(SpringJUnit4ClassRunner.class)
#RunWith(Parametrized.class)
#ContextConfiguration("/META-INF/blah-spring-test.xml")
public class BlahTest
..
so i want to have a spring nature test and at the same time want to have it parameterized to avoid code duplication ...
You can't use two runners as it noted in the commented post. You should use the Parameterized runner as use Spring's TestContextManager to load the Spring context.
#Before
public void before() throws Exception {
new TestContextManager(getClass()).prepareTestInstance(this);
}
As of Spring Framework 4.2, JUnit-based integration tests can now be executed with JUnit rules instead of the SpringJUnit4ClassRunner. This allows Spring-based integration tests to be run with alternative runners like JUnit’s Parameterized or third-party runners such as the MockitoJUnitRunner. See more details on spring doc.
Building upon John B's answer using TestContextManager, one can also call beforeTestMethod() and afterTestMethod() on it to better simulate SpringJUnit4ClassRunner's behaviour (for instance loading database with #Sql).
These methods require a Method parameter, so one can for instance take advantage of JUnit4's TestName rule to get the current test method's name and then retrieving it by reflection.
private static TestContextManager springTestContext
= new TestContextManager(BlahTest.class);
#Rule
public TestName testName = new TestName();
#Before
public void before() throws Exception {
springTestContext.prepareTestInstance(this);
springTestContext.beforeTestMethod(this,
getClass().getMethod(testName.getMethodName()));
}
#After
public void after() throws Exception {
springTestContext.afterTestMethod(this,
getClass().getMethod(testName.getMethodName()), null);
}

How to prevent test from cleaning up db before each test in TestNG?

I have a test like this:
#RunWith(SpringJUnit4ClassRunner.class),
#ContextConfiguration(locations = { "file:war/WEB-INF/application-context.xml" })
#Transactional
public class ServiceImplTest extends AbstractTestNGSpringContextTests
{
#Autowired
private Service service;
#Test
#Rollback(false)
public void testCreate()
{
.....
//save an entity to table_A
service.save(a);
}
}
It seems that the table_A will be cleaned up before each test running(not roolback after test ran),because after each test,all old data entries in the table are cleaned up,only new inserted entry by test is left.How to prevent this "cleaning" action?
The default behavior is to rollback the transactions in testing context. You can override this behavior using the #Rollback(false) annotation on a test method to not rollback the changes made to the DB during that particular test.
That said, it is recommended that each test case is independent and should have its own scenario setup, scenario execution and scenario tear down. Otherwise, the test failure behavior would be difficult to analyze if there are inter-dependencies among tests.

junit 4 TransactionalTestExecutionListener insert test data only once for all tests in class?

I have a junit 4 test class testing a DAO.
unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:/WEB-INF/applicationContext-db.xml",
"classpath:/WEB-INF/applicationContext-hibernate.xml",
"classpath:/WEB-INF/applicationContext.xml" })
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
#DataSetLocation("test/java/com/yada/yada/dao/dbunit-general.xml")
#TransactionConfiguration(transactionManager="transactionManager", defaultRollback = true)
#Transactional
public class RealmDAOJU4Test {
#Autowired
private DbUnitInitializer dbUnitInitializer;
#Autowired
private RealmDAO realmDAO;
#BeforeTransaction
public void setupDatabase() {
// use dbUnitInitializer to insert test data
}
#Test
public void testGetById() {
Integer id = 2204;
Realm realm = realmDAO.get(id);
assertEquals(realm.getName().compareToIgnoreCase(
"South Technical Realm"), 0);
assertEquals(8, realm.getRealmRelationships().size());
}
// more test methods annotated here
}
The #BeforeTransacation method runs before EVERY test method. What I would like to do is: use my DbUnitInitializer to load data into my database - ONCE when the class is created. Then have each test in the class do what it needs to do with the database, then roll back (not commit) it's changes. It seems over kill to re-insert all the same data from my test files before EVERY test. Is there a way to accomplish this?
or
Is the correct way to write these tests to completely load the database before EVERY test? If so, what function does the defaultRollback=true have in this situation?
thanks for helping me along in my thinking...
You need to use a TestExecutionListener and set up your database in the beforeTestClass method. See the Annotations section of the Testing chapter in the Spring user guide.

Resources