We are using dbunit with spring boot and H2 database for local unit/integration testing. In the test we are using TransactionDbUnitTestExecutionListener and #Transactional annotations so that the transaction during the test does not rollback until the end.
However, I observed that if in the test I call a function with Propogation.REQUIRES_NEW, the rollback occurs immediately after the function returns and then test fails because it cannot find the data.
Here is the example code
#Test
#Transactional
#DatabaseSetup("/Uploads.xml")
#Rollback(value = false)
fun testStampSidekiqJobId() {
uploadEntity = uploadService.stampSidekiqJobId(uploadEntity, "fakeJobId")
// test values
}
// here is the called function
#Transactional(propagation = Propagation.REQUIRES_NEW)
open fun stampSidekiqJobId(uploadEntity:UploadEntity, encodeJobId:String): UploadEntity {
I have tried with and without Rollback annotation. Please note that the class hosting this unit test has DependencyInjectionTestExecutionListener, TransactionDbUnitTestExecutionListener annotations.
Related
I am reading Spring docs about Spring test in: here
And about using #Transactinoal on tests it says:
If your test is #Transactional, it rolls back the transaction at the end of each test method by default. However, as using this arrangement with either RANDOM_PORT or DEFINED_PORT implicitly provides a real servlet environment, the HTTP client and server run in separate threads and, thus, in separate transactions. Any transaction initiated on the server does not roll back in this case.
I do not understand what does it exactly means by Any transaction initiated on the server does not roll back in this case.
Any help is appreciated.
It means that your server won't rollback your changes because it will run in another environment than the test environment.
Only changes you have made in your test environment would be rollbacked.
For instance:
#Autowired
private AnyRepository anyRepository;
#Test
#Transactional
void testSave(){
anyRepository.save(new AnyEntity());
// Will create an entity from your test environment
}
#Test
#Transactional
void testRead(){
anyRepository.findAll();
// Won't find any entities since they were rollbacked
}
On the contrary, if you launched a local instance of Spring using #SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) for instance), it is detached from your unit test environment, hence:
#Autowired
MockMvc mvc;
#Test
#Transactional
void testSave(){
mvc.perform(post(/* enough to create an entity */);
// Your server, detached from test environment, persists the entity
}
#Test
#Transactional
void testRead(){
mvc.perform(get(/* enough to get that entity */);
// Will get previously created entity (if testSave was run before)
}
If you want to rollback after sending a web request, you could use the #DirtiesContext annotation to reset your context, or check Reset database after each test on Spring without using DirtiesContext.
edit: following comments on original post, it was not clear whether you needed to use WebEnvironment.RANDOM_PORT or if it was a simple question.
Most likely, if you do not need WebEnvironment.RANDOM_PORT, you can simply use WebEnvironment.MOCK, which runs in the same environment that the JUnit tests, hence would actually rollback.
I'm using Spring Boot and trying to write an integration test in which a method annotated: #Transactional(propagation = Propagation.REQUIRES_NEW) is called.
My test method is annotated:
#Test
#Transactional
#Sql(scripts = {
"/sql/insert_some_records.sql"
}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
With this configuration I'm getting a strange behaviour and when I tried to debug I found out that the service bean class method I'm trying to test can't find the records I've inserted using the sql script in the #Sql i.e.
myRepo.findAll() inside the method gets an empty list.
I've managed to run the test after changing the propagation option for the test method to:
#Transactional(propagation = Propagation.NEVER)
or:
#Transactional(propagation = Propagation.SUPPORTS)
And the method in the service class can correctly find all records from DB, but the drawback is that I have to manually delete these inserted records by the script after the test.
I've read Spring documentation about Propagation option and still really confused.
Can anyone explain for me what's the cause of the problem? And why everything works after changing the propagation option?
when test run, it create a transaction T1 by default if with annotation #Trasactional, and your service method create another transaction T2 per REQUIRE_NEW, since T1 has not commit util test over, T2 can not see the data that has not been committed. but if test is without #Transactional , or NEVER or SUPPORT, it will not create a transaction, but the sql was execute by jdbcTemplate.execute(), it will create a T1 and commit immediately after execute() return. so service method is able to see the data.
Following my general question I have a specific issue using spring jdbcTemplate , I want to rollback specific test method after every execution of DAO method below.
Adding #Transactional and #Rollback(true) failed to rollback insert
Also getting connection before/after and rollback it doesn't effect
#Test
#Transactional
#Rollback(true)
public void testInsertUser() {
Assert.assertEquals(userDAO.insertUser(new User(55616103, true, true, false)), true);
}
How should I rollback unit test using TestNG framework? most answers use Junit's #RunWith(SpringJUnit4ClassRunner.class)
I failed auto wiring the jdbcTemplate using TestNG:
#Autowired
private JdbcTemplate jdbcTemplate;
But succeeded using SpringJUnit4ClassRunner with including Configuration class includes jdbcTemplate/DataStource
Do TestNG have option to execute using Spring context?
The solution is to replace the AbstractTestNGSpringContextTests with AbstractTransactionalTestNGSpringContextTests.
reference: Spring + TestNG not transactionally rollback
In spring boot I'm trying to create my first transactional test, but the trasaction doesn't work.
#TestPropertySource(locations = "classpath:test.properties")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#RunWith(SpringJUnit4ClassRunner.class)
//#RunWith(SpringRunner.class)
#Transactional
#TestExecutionListeners(
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
listeners = {TransactionalTestExecutionListener.class}
)
public class IntegrationTests
{
#Autowired
TemperatureLogRepository temperatureLogRepository;
#Test
#SqlGroup({
#Sql(
executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
config = #SqlConfig(transactionMode = ISOLATED),
scripts = "classpath:sqls/insertRecords2.sql"
)
})
public void firstRepoTest() throws SQLException
{
assertThat(temperatureLogRepository.getFullList().size()).isEqualByComparingTo(0);
}
}
I know that SqlGroup is not necessary, but there will be more files added.
That I have now:
SQL file executed well and inserted to the DB.
The getFullList() method can read it and returns with the right data.
After the test I still have the data in the DB, there is no rollback on the transaction.
I'm not exactly sure they are running in the same transaction. Is it possible to be the data commited to the db before the getFullList() method run?
What I need:
#Sql inserts data to the transaction.
getFullList() read the data from the transaction.
Test the returned data.
Rollback the transaction.
From Spring Testing - Executing SQL scripts declaratively with #Sql:
Script execution phases
By default, SQL scripts will be executed before the corresponding test
method. However, if a particular set of scripts needs to be executed
after the test method — for example, to clean up database state — the
executionPhase attribute in #Sql can be used as seen in the following
example. Note that ISOLATED and AFTER_TEST_METHOD are statically
imported from Sql.TransactionMode and Sql.ExecutionPhase respectively.
#Test
#Sql(
scripts = "create-test-data.sql",
config = #SqlConfig(transactionMode = ISOLATED) ) #Sql(
scripts = "delete-test-data.sql",
config = #SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD )
public void userTest {
// execute code that needs the test data to be committed
// to the database outside of the test's transaction
}
Related question: How to execute #Sql before a #Before method
UPDATE
Remove #SqlConfig from #Sql:
config = #SqlConfig(transactionMode = ISOLATED)
Or change to:
config = #SqlConfig(transactionMode = TransactionMode.INFERRED)
SQL script runs in separate transaction which is not roll backed:
org.springframework.test.context.jdbc.SqlConfig.TransactionMode.ISOLATED
Indicates that SQL scripts should always be executed in a new,
isolated transaction that will be immediately committed.
I am using a combination of Spring and Hibernate in my project and would like to test the DAO methods like Save and Delete methods.
daoFoundation is a wrapper class created over hibernateSession.
#Override
public String createSubject(Subject subject) {
String subjectId = (String) daoFoundation.save(subject);
return subjectId;
}
This is what I wrote in my JUnit Runs with SpringJunit4ClassRunner
I created the subject object in my SetupMethod.
#Test
public void createSubjectTest(){
subjectDao.createSubject(subject);
assertNotNull(hassSubjectSelection.getId());
}
Is this sufficient or do I need to write anything additional in my test class?
The easiest way is to import your Spring application context, autowire in the DAO's you want to test and then mark either your test methods or the entire class as #Transactional. This will create a Hibernate session, run your test and then automatically roll back the transaction so you don't effect your database state with your tests.
Have a look at how to run unit tests with Spring here. You can get Spring to create your entire application context using the #ContextConfiguration annotation. So if you create your database using an XML file called database-servlet.xml then you would annotate
#ContextConfiguration(locations={"classpath:/database-servlet.xml"})
public class Test()
You can use the annotation #RunWith(SpringJUnit4ClassRunner.class) to use functionality of the Spring TestContext Framework with your unit tests. This allows you to do things like declare expected exceptions that should be thrown, run timed tests, repeat test runs X times and a bunch of other cool stuff.
Basically to get this working, your test class should look similar to the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={YOUR APP CONTEXT FILES HERE AS A COMMA SEPARATED LIST})
public class Test(){
#Autowired
private YourDAO yourDAO;
#Test
#Transactional
public void testSave(){
//Test save method here. Any database changes made here will be
//automatically rolled back when the test finishes.
}
Let me know if that works or not.
The best way to test your dao layer is to use the spring jdbctemplate to write data to your database the test your get and delete methods. Then in the #after delete the records you wrote. Then use hibernate to write to your database and use jdbctemplate to read them back. Then delete your test rows. Anything less and all you are really doing is testing hibernate's caching.