springtestdbunit / spring-test-dbunit

Integration between the Spring testing framework and DBUnit
http://springtestdbunit.github.com/spring-test-dbunit/
Apache License 2.0
476 stars 238 forks source link

Expected database doesn't work with transactional and delete #75

Closed nyddogghr closed 9 years ago

nyddogghr commented 9 years ago

When using @Transactional, TransactionDbUnitTestExecutionListener.class and @ExpectedDatabase, updating a row makes the expected database to always fail.

Here is a exemple :

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = MvcUnitTestConfiguration.class)
@DatabaseSetup("uuidtest.xml")
@DatabaseTearDown(value = "uuidtest.xml", type = DatabaseOperation.DELETE)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionDbUnitTestExecutionListener.class })
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class TestDUITransactional {

@Autowired
private WebApplicationContext   wac;
@Autowired
TempUuidDaoI tempUuidDaoI;

private MockMvc mockMvc;

@Before
public void setup() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}

@Test
@ExpectedDatabase(value = "testDelete.xml", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED)
public void testDeleteUuid() throws Exception {
    Integer nb = tempUuidDaoI.getAll().size();
    tempUuidDaoI.delete(tempUuidDaoI.get(1));
    assertEquals(nb-1, tempUuidDaoI.getAll().size());
}

@Test
@ExpectedDatabase(value = "testUpdate.xml", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED)
public void testUpdateUuid() throws Exception {
    TempUuid tu = tempUuidDaoI.get(1);
    tu.setUuid("4b2f02ca-d857-4c95-b7fa-ee6283ce0d66");
    tempUuidDaoI.update(tu);
    tu = tempUuidDaoI.get(1);
    assertEquals("4b2f02ca-d857-4c95-b7fa-ee6283ce0d66", tu.getUuid());
}

@Test
@ExpectedDatabase(value = "testInsert.xml", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED)
public void testInsertUuid() throws Exception {
    Integer nb = tempUuidDaoI.getAll().size();
    TempUuid tu = new TempUuid();
    tu.setUuid("4b2f02ca-d857-4c95-b7fa-ee6283ce0d67");
    tempUuidDaoI.insert(tu);
    assertEquals(nb+1, tempUuidDaoI.getAll().size());
}

}

In this code, only the update doesn't work.

The original db :

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <PDI.TEMP_UUIDS tuu_id="1" uuid="4b2f02ca-d857-4c95-b7fa-ee6283ce0d64" />
    <PDI.TEMP_UUIDS tuu_id="2" uuid="4b2f02ca-d857-4c95-b7fa-ee6283ce0d65" />
</dataset>

testDelete.xml :

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <PDI.TEMP_UUIDS tuu_id="2" uuid="4b2f02ca-d857-4c95-b7fa-ee6283ce0d65" />
</dataset>

testUpdate.xml :

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <PDI.TEMP_UUIDS tuu_id="1" uuid="4b2f02ca-d857-4c95-b7fa-ee6283ce0d66" />
    <PDI.TEMP_UUIDS tuu_id="2" uuid="4b2f02ca-d857-4c95-b7fa-ee6283ce0d65" />
</dataset>

testInsert.xml :

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <PDI.TEMP_UUIDS tuu_id="1" uuid="4b2f02ca-d857-4c95-b7fa-ee6283ce0d64" />
    <PDI.TEMP_UUIDS tuu_id="2" uuid="4b2f02ca-d857-4c95-b7fa-ee6283ce0d65" />
    <PDI.TEMP_UUIDS tuu_id="3" uuid="4b2f02ca-d857-4c95-b7fa-ee6283ce0d67" />
</dataset>

I've checked that the methods to update/delete/insert work, as does the dao. I use postgresql for anything else than unit testing for the project. I use hibernate H2 for testing.

Any idea on where does this come from ?

ekravchenko commented 9 years ago

Hi nyddogghr,

The issue that you described is very interesting. I created a sample application that shows this problem: https://github.com/ekravchenko/testingzone/tree/master/spring-dbunit-issue75

In the sample application I'm using same entity and dataset files that you provided in the example. Instead of DAO I'm using spring-data-jpa CrudRepository but that shouldn't matter cause I could reproduce the issue you described.

If I understand you right testInsert and testDelete work but testUpdate fails. The root of the problem is the @Transactional annotation which causes the tests to be wrapped in the transaction. DBUnit checks the expected dataset however data is not inserted/updated cause it only happens at the end of transaction. If this statement is correct then none of your tests should work.

The reasonable questions is 'why testInsert and testDelete work then'? They work because you have the following line in tests:

assertEquals(nb-1, tempUuidDaoI.getAll().size());

If you don't configure FlushMode then the default value of AUTO will be used. So when you fire tempUuidDaoI.getAll() at the end of the test it makes hibernate flush before transaction ends, thus making changes visible for DBUnit checks.

Re-write testUpdateUuid in the following way and you will be surprised to find it working:

@Test
@ExpectedDatabase(value = "testUpdate.xml", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED)
public void testUpdateUuid() throws Exception {
TempUuid tu = tempUuidDaoI.get(1);
tu.setUuid("4b2f02ca-d857-4c95-b7fa-ee6283ce0d66");
tempUuidDaoI.update(tu);
tu = tempUuidDaoI.get(1);
assertEquals("4b2f02ca-d857-4c95-b7fa-ee6283ce0d66", tu.getUuid());
tempUuidDaoI.getAll();
}

The last line will force the flush.

As a workaround I would suggest flushing manually at the end of the test. session.flush() or entityManager.flush(). I wouldn't rely on AUTO flush cause that's not intuitive.

However it would be awesome if this problem could be fixed on the tesing framework level so that users wouldn't have to worry about it.

nyddogghr commented 9 years ago

Hi,

Thank you for your anwser. I have retried this, with your correction, but I'm not able to fix this. I even tried flushing manually :

    @Test
    @ExpectedDatabase(value = "testUpdate.xml", assertionMode = DatabaseAssertionMode.NON_STRICT_UNORDERED)
    public void testUpdateUuid() throws Exception {
        TempUuid tu = tempUuidDaoI.get(1);
        tu.setUuid("4b2f02ca-d857-4c95-b7fa-ee6283ce0d66");
        tempUuidDaoI.update(tu);
        tu = tempUuidDaoI.get(1);
        assertEquals("4b2f02ca-d857-4c95-b7fa-ee6283ce0d66", tu.getUuid());
        tempUuidDaoI.getAll();
        tempUuidDaoI.flush();
    }

But I still have the same error. What tempUuidDaoI.flush() does is simply getSession.flush(). So it should have fixed it. I remember I had also tried this with no result.

So far, I have no anwser, I had to use another way to do what I wanted to do (use jdbc template to avoid the @Transactionnal annotation).

Thanks again even if I can't confirm this is fixed

ekravchenko commented 9 years ago

HI nyddogghr,

Could you please send a link to your sample application in Git? That would really help in sorting this issue out.

Thanks, YK

nyddogghr commented 9 years ago

Hi,

Sorry but for some reason I'm not able to create a sample application for this. I've tried your sample that indeed works, but I have used another way to get past this.

Once again, thank you for your help. I'm closing this since it seems it comes from my project only.

inaumov commented 6 years ago

I have the same trouble :( Without flush it does not work

patient-developer commented 1 year ago

Hi, with the help of flush I was able to make an integration test work on service-level, e.g., see: service integration test

However, it does not work on Mvc-level, when testing the controller, e.g., see: controller integration test

Do you have any idea what is the issue with the MockMvc? Can you advice?

Thanks in advance!