How to execute #Sql before a #Before method - spring

I'm trying to combine the follow annotations:
org.springframework.test.context.jdbc.Sql
and
org.junit.Before
Like the follow code:
#Test
#Sql(scripts = "dml-parametro.sql")
public void testData(){
Iterable<Parametro> parametros = parametroService.findAll();
List<Parametro> parametrosList = Lists.newArrayList(parametros);
Assert.assertThat(parametrosList.size(), Is.is(1));
}
#Before
public void beforeMethod() {
JdbcTestUtils.deleteFromTables(jdbcTemplate, "PARAMETRO");
}
The code in the method #Before is running after then the script "dml-parametro.sql" in the #Sql annotation.
Is it right to do this?
For solution this, I'm using #After in place than #Before, but I'd like to cdelete tables before the test execution, not after.
I wouldn't like to use #SqlConfig. I'm not using transacional scope on test level, so i need to clean my tables in every test method. If every test method need to clean tables, i would like to do this in #Before method. I wouldn't like to do this in every test method with #SqlConfig. I think the behavior of #Sql to be execute before than #Before is wrong.

By default, any SQL scripts executed via #Sql will be executed before any #Before methods. So the behavior you are experiencing is correct, but you can change the execution phase via the executionPhase attribute in #Sql (see example below).
If you want to execute multiple scripts, that is also possible via #Sql.
So if you have a clean-up script named clean-parametro.sql that deletes from the PARAMETRO table, you could annotate your test method like the following (instead of invoking JdbcTestUtils.deleteFromTables() in your #Before method).
#Test
#Sql({"dml-parametro.sql", "clean-parametro.sql"})
public void test() { /* ... */ }
Of course, if dml-parametro.sql inserts values into the PARAMETRO table, then it likely does not make sense to immediately delete those values in the clean-up script.
Please note that #Sql and #SqlConfig provide multiple levels of configuration for script execution.
For example, if you want to create tables before your test and clean up after your test, you could do something like this on Java 8:
#Test
#Sql("create-tables.sql")
#Sql(scripts = "clean-up.sql", executionPhase = AFTER_TEST_METHOD)
public void test() { /* ... */ }
Or use #SqlGroup as a container on Java 6 or Java 7:
#Test
#SqlGroup({
#Sql("create-tables.sql"),
#Sql(scripts = "clean-up.sql", executionPhase = AFTER_TEST_METHOD)
})
public void test() { /* ... */ }
If your tests are #Transactional and you'd like to clean up committed database state, you can instruct Spring to execute your clean-up SQL script in a new transaction like this:
#Test
#Sql("insert-test-data.sql")
#Sql(
scripts = "clean-up.sql",
executionPhase = AFTER_TEST_METHOD,
config = #SqlConfig(transactionMode = ISOLATED)
)
public void test() { /* ... */ }
I hope this clarifies things for you!
Cheers,
Sam (author of the Spring TestContext Framework)
Notes:
AFTER_TEST_METHOD is statically imported from ExecutionPhase
ISOLATED is statically imported from TransactionMode

Related

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

How to populate test data programmatically for integration tests in 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.

#SQL one time per class

I'm writing some integration test using spring framework. I have different SQL scripts for different integration test classes. Something like this:
#ContextConfiguration(classes = ...)
#Sql("classpath:sportCenter-test.sql")
public class SportCenterResourceIT {
...
}
Everything works perfectly except for the fact that the SQL script is executed before each test, instead of one time per class. I have already searched for some time the spring documentation but I was not able to find something related to such an option.
Could anybody give me an hint?
I use this way to resolve the problem:
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
#Sql("classpath:sql/DataForRouteTesting.sql")
public class MyTest {}
Scripts are executed every time, but there are not conflict primary key.
Adding the org.springframework.transaction.annotation.Transactional annotation at the class level will prevent any changes from #Sql scripts from persisting between tests. So your code becomes:
#ContextConfiguration(classes = ...)
#Sql("classpath:sportCenter-test.sql")
#Transactional
public class SportCenterResourceIT {
...
}
This combination will result in the following:
Your #Sql script will run before each test
The test itself will run
Any database changes from steps 1 or 2 will be reverted at the end of each test
If we want to execute sql script one time per class, we can execute the script in setup method and set flag to true once the script is executed for one method so it does not execute again.
`
#Autowired
private DataSource database;
private static boolean dataLoaded = false;
#Before
public void setup() throws SQLException {
if(!dataLoaded) {
try (Connection con = database.getConnection()) {
ScriptUtils.executeSqlScript(con, new ClassPathResource("path_to_script.sql"));
dataLoaded = true;
}
}
}`
I solved my problem by putting the sql script in the src/test/resources folder with the name data.sql. This file is executed once at startup of each integration test. For this to work, you need to remove the #Sql annotation.
You can create an empty static method annotated with #BeforeAll and #Sql("classpath:sportCenter-test.sql")
#BeforeAll
#Sql("classpath:sportCenter-test.sql")
public static void initSql() {
}

How to priortized the test cases in maven run?

I have 3 test cases i.e. 1 2 3. How will i give priority as 2 1 3 while executing maven command.
I assume you want to do it because you need some prerequisition prior the test can be run. You can do it by #Before Annotation prior the actual testcase, or you can call other tests from the test method.
Say, testClient() test will test and verify that new client can be added to the system. Then you can do this:
#Test
public void testWithdrawal(){
testClient(); // i need client existing before the test can be run
// ... do something else
}
In that case you have assured that prerequisites are fullfilled and dont have to worry much about the testcases order
EDIT
I think I understand your needs, because I am in quite similar situation. How I solved it:
For create I have special class, which can create me a data and return needed data. So, i have something like:
#Test
public void testShare(){
CreateTests create = new CreateTests; //This will just initialize the object
create.testCreate(); // this method can contain steps needed to create
String justCreatedEntity = create.getEntity(); // just example how you can use the just created entity in further tests
}
And my class to solve the create is something like this
public class CreateTests{
private static String entity; //static because i dont want it to be flushed when test ends
public void testCreate() throws Exception{
WebDriverBackedSelenium selenium = new WebDriverBackedSelenium(driver, "baseURL");
selenium.... // All the selenium stuff
setEntity(selenium.getText("id=mainForm.createdentity"));
}
public void setEntity(String ent){
this.entity = ent;
}
public String getEntity(){
return entity;
}
Its just an outline - but basically, I have these "crucial" entities as standalone objects, called by the test class. Inside test, i verify everything throgh getters. Like:
Assert.assertNotNull(create.getAuctionID(),"New Entity is NULL!" );
You can run mvn test with options to specify a single test, or multiple tests. The order they are run in is the order specified on the command line.
Reference is here: http://maven.apache.org/plugins/maven-surefire-plugin/examples/single-test.html
Note that Java suggests that the good unit testing practice of tests not requiring to be run in order and test to not rely on each other:
http://java.sun.com/developer/Books/javaprogramming/ant/ant_chap04.pdf

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