开发者

Functional test in Spring: changes in database not visible in test

I have integration test (which runs under Jetty) where I open page (by using Selenium) and check that record about this activity was added to database (HSQL). But it does not work -- JPA (Hiberante) adds record (I see it in logs) but when 开发者_C百科I execute SELECT query there no records at all.

Test case:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
    "classpath:spring/DispatcherServletContext.xml"
})
@TransactionConfiguration(defaultRollback = false)
public class WhenUserOpenNotExistingPage
    extends WhenUserAtAnyPage<NotFoundErrorPage> {

    private final String currentUrl;

    @Autowired
    private SuspiciousActivityDao suspiciousActivities;

    private String generateRandomUrl() {
        return String.format(
            "/tests/page-does-not-exists-%s.htm",
            RandomStringUtils.randomNumeric(5)
        );
    }

    public WhenUserOpenNotExistingPage() {
        currentUrl = generateRandomUrl();
        page.open(currentUrl);
    }

    @Test
    @Transactional(readOnly = true)
    public void incidentShouldBeLoggedToDatabase() {
        SuspiciousActivity activity =
            suspiciousActivities.findByPage(currentUrl);

        assertNotNull(activity);
    }
}

Also WhenUserOpenNotExistingPage() (constructorr) called twice (and I don't know why it happens and probably is the root of my problem).

Can you help me? Thanks in advance!


I assume you are adding something to the database in the test case and the same database is being used by your application running on Jetty. If your database uses any isolation level above read uncommited, the changes you made in the test case won't be visible until that test case finishes. This is because you database code joins the transaction that was created when the test was starting.

By default this test transaction is rolled back after the test finishes, so the changes are visible within the current test (transaction), but aren't visible outside (by different threads/connections) and are rolled back. You are changing the default behaviour by using defaultRollback = false attribute, but this only means that changes you made in one test aren't visible by the web application (different database connection), but will be visible in subsequent test (after commit). Not really useful.

You have few options:

  • Get rid of Spring transactional test support. This means Spring won't create a new transaction every time you start a test and won't do commit/rollback. Now it is up to you when to start transaction and commit it before actually starting SElenium test.

This can easily be done by replacing @TransactionConfiguration with:

@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})

this will override default test execution listeners, removing TransactionalTestExecutionListener from the stack

  • You might run setup code from different thread or with propagation REQUIRES_NEW

  • Finally, you should consider doing setup outside of JUnit. Maybe you can do the setup from the application itself?

As for the constructor - new instance of JUnit test class is created per test. The authors of JUnit claim it makes the test more predictable and stateless (no dependencies between test) by cleaning up the test class prior to running every test. In practice, especially with integration tests, this is more a pain than a advantage.

By the way if you do some database manipulation in the test and commit the changes, watch out for test dependencies. The order in which tests are executed is not guaranteed in JUnit. This means that changes in one test shouldn't affect other tests.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜