Skip to content

Commit c0eafa9

Browse files
committed
Introduce EJB-based transactional tests in the TCF
This commit introduces transactional integration tests executing against both JUnit and TestNG in the TestContext framework (TCF) using @TransactionAttribute in EJBs instead of Spring’s @transactional annotation. These tests disprove the claims raised in SPR-6132 by demonstrating that transaction support in the TCF works as expected when a transactional EJB method that is configured with TransactionAttribute.REQUIRES_NEW is invoked. Specifically: - The transaction managed by the TCF is suspended while such an EJB method is invoked. - Any work performed within the new transaction for the EJB method is committed after the method invocation completes. - The transaction managed by the TCF is resumed and subsequently either rolled back or committed as necessary based on the configuration of @Rollback and @TransactionConfiguration. The configuration for the JUnit-based tests is straightforward and self explanatory; however, the configuration for the TestNG tests is less intuitive. In order for the TCF to function properly, the developer must ensure that test methods within a given TestNG test (whether defined locally, in a superclass, or somewhere else in the suite) are executed in the proper order. In a stand-alone test class this is straightforward; however, in a test class hierarchy (or test suite) with dependent methods, it is necessary to configure TestNG so that all methods within an individual test are executed in isolation from test methods in other tests. This can be achieved by configuring a test class to run in its own uniquely identified suite (e.g., by annotating each concrete TestNG-based test class with @test(suiteName = "< Some Unique Suite Name >")). For example, without specifying a unique suite name for the TestNG tests introduced in this commit, test methods will be executed in the following (incorrect) order: - CommitForRequiredEjbTxDaoTestNGTests.test1InitialState() - CommitForRequiresNewEjbTxDaoTestNGTests.test1InitialState() - RollbackForRequiresNewEjbTxDaoTestNGTests.test1InitialState() - RollbackForRequiredEjbTxDaoTestNGTests.test1InitialState() - CommitForRequiredEjbTxDaoTestNGTests.test2IncrementCount1() The reason for this ordering is that test2IncrementCount1() depends on test1InitialState(); however, the intention of the developer is that the tests for an individual test class are independent of those in other test classes. So by specifying unique suite names for each test class, the following (correct) ordering is achieved: - RollbackForRequiresNewEjbTxDaoTestNGTests.test1InitialState() - RollbackForRequiresNewEjbTxDaoTestNGTests.test2IncrementCount1() - RollbackForRequiresNewEjbTxDaoTestNGTests.test3IncrementCount2() - CommitForRequiredEjbTxDaoTestNGTests.test1InitialState() - CommitForRequiredEjbTxDaoTestNGTests.test2IncrementCount1() - CommitForRequiredEjbTxDaoTestNGTests.test3IncrementCount2() - RollbackForRequiredEjbTxDaoTestNGTests.test1InitialState() - RollbackForRequiredEjbTxDaoTestNGTests.test2IncrementCount1() - RollbackForRequiredEjbTxDaoTestNGTests.test3IncrementCount2() - CommitForRequiresNewEjbTxDaoTestNGTests.test1InitialState() - CommitForRequiresNewEjbTxDaoTestNGTests.test2IncrementCount1() - CommitForRequiresNewEjbTxDaoTestNGTests.test3IncrementCount2() See the JIRA issue for more detailed log output. Furthermore, @DirtiesContext(classMode = ClassMode.AFTER_CLASS) has been used in both the JUnit and TestNG tests introduced in this commit in order to ensure that the in-memory database is reinitialized between each test class. Issue: SPR-6132
1 parent 597ef09 commit c0eafa9

22 files changed

+938
-0
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,9 @@ project("spring-test") {
804804
testCompile(project(":spring-context-support"))
805805
testCompile(project(":spring-oxm"))
806806
testCompile(project(":spring-webmvc-tiles3"))
807+
testCompile("javax.ejb:ejb-api:3.0")
807808
testCompile("org.hibernate:hibernate-core:${hibernate3Version}")
809+
testCompile("org.hibernate:hibernate-entitymanager:${hibernate3Version}")
808810
testCompile "org.slf4j:slf4j-jcl:${slf4jVersion}"
809811
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")
810812
testCompile("org.hibernate:hibernate-validator:4.3.0.Final")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.testng.transaction.ejb;
18+
19+
import javax.ejb.EJB;
20+
21+
import javax.persistence.EntityManager;
22+
import javax.persistence.PersistenceContext;
23+
24+
import org.springframework.test.annotation.DirtiesContext;
25+
import org.springframework.test.annotation.DirtiesContext.ClassMode;
26+
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
27+
import org.springframework.test.context.transaction.ejb.dao.TestEntityDao;
28+
29+
import org.testng.annotations.AfterMethod;
30+
import org.testng.annotations.Test;
31+
32+
import static org.testng.AssertJUnit.*;
33+
34+
/**
35+
* Abstract base class for all TestNG-based tests involving EJB transaction
36+
* support in the TestContext framework.
37+
*
38+
* @author Sam Brannen
39+
* @author Xavier Detant
40+
* @since 4.0.1
41+
*/
42+
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
43+
public abstract class AbstractEjbTxDaoTestNGTests extends AbstractTransactionalTestNGSpringContextTests {
44+
45+
protected static final String TEST_NAME = "test-name";
46+
47+
@EJB
48+
protected TestEntityDao dao;
49+
50+
@PersistenceContext
51+
protected EntityManager em;
52+
53+
54+
@Test
55+
public void test1InitialState() {
56+
int count = dao.getCount(TEST_NAME);
57+
assertEquals("New TestEntity should have count=0.", 0, count);
58+
}
59+
60+
@Test(dependsOnMethods = "test1InitialState")
61+
public void test2IncrementCount1() {
62+
int count = dao.incrementCount(TEST_NAME);
63+
assertEquals("Expected count=1 after first increment.", 1, count);
64+
}
65+
66+
/**
67+
* The default implementation of this method assumes that the transaction
68+
* for {@link #test2IncrementCount1()} was committed. Therefore, it is
69+
* expected that the previous increment has been persisted in the database.
70+
*/
71+
@Test(dependsOnMethods = "test2IncrementCount1")
72+
public void test3IncrementCount2() {
73+
int count = dao.getCount(TEST_NAME);
74+
assertEquals("Expected count=1 after test2IncrementCount1().", 1, count);
75+
76+
count = dao.incrementCount(TEST_NAME);
77+
assertEquals("Expected count=2 now.", 2, count);
78+
}
79+
80+
@AfterMethod(alwaysRun = true)
81+
public void synchronizePersistenceContext() {
82+
em.flush();
83+
}
84+
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.testng.transaction.ejb;
18+
19+
import org.springframework.test.context.ContextConfiguration;
20+
import org.springframework.test.context.transaction.TransactionConfiguration;
21+
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
22+
import org.springframework.test.context.transaction.ejb.dao.RequiredEjbTxTestEntityDao;
23+
import org.testng.annotations.Test;
24+
25+
/**
26+
* Concrete subclass of {@link AbstractEjbTxDaoTestNGTests} which uses the
27+
* {@link RequiredEjbTxTestEntityDao} and sets the default rollback semantics
28+
* for the {@link TransactionalTestExecutionListener} to {@code false} (i.e.,
29+
* <em>commit</em>).
30+
*
31+
* @author Sam Brannen
32+
* @since 4.0.1
33+
*/
34+
@Test(suiteName = "Commit for REQUIRED")
35+
@ContextConfiguration("/org/springframework/test/context/transaction/ejb/required-tx-config.xml")
36+
@TransactionConfiguration(defaultRollback = false)
37+
public class CommitForRequiredEjbTxDaoTestNGTests extends AbstractEjbTxDaoTestNGTests {
38+
39+
/* test methods in superclass */
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.testng.transaction.ejb;
18+
19+
import org.springframework.test.context.ContextConfiguration;
20+
import org.springframework.test.context.transaction.TransactionConfiguration;
21+
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
22+
import org.springframework.test.context.transaction.ejb.dao.RequiresNewEjbTxTestEntityDao;
23+
import org.testng.annotations.Test;
24+
25+
/**
26+
* Concrete subclass of {@link AbstractEjbTxDaoTestNGTests} which uses the
27+
* {@link RequiresNewEjbTxTestEntityDao} and sets the default rollback semantics
28+
* for the {@link TransactionalTestExecutionListener} to {@code false} (i.e.,
29+
* <em>commit</em>).
30+
*
31+
* @author Sam Brannen
32+
* @since 4.0.1
33+
*/
34+
@Test(suiteName = "Commit for REQUIRES_NEW")
35+
@ContextConfiguration("/org/springframework/test/context/transaction/ejb/requires-new-tx-config.xml")
36+
@TransactionConfiguration(defaultRollback = false)
37+
public class CommitForRequiresNewEjbTxDaoTestNGTests extends AbstractEjbTxDaoTestNGTests {
38+
39+
/* test methods in superclass */
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.testng.transaction.ejb;
18+
19+
import org.springframework.test.context.transaction.TransactionConfiguration;
20+
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
21+
import org.testng.annotations.Test;
22+
23+
import static org.testng.AssertJUnit.*;
24+
25+
/**
26+
* Extension of {@link CommitForRequiredEjbTxDaoTestNGTests} which sets the default
27+
* rollback semantics for the {@link TransactionalTestExecutionListener} to
28+
* {@code true}. The transaction managed by the TestContext framework will be
29+
* rolled back after each test method. Consequently, any work performed in
30+
* transactional methods that participate in the test-managed transaction will
31+
* be rolled back automatically.
32+
*
33+
* @author Sam Brannen
34+
* @since 4.0.1
35+
*/
36+
@Test(suiteName = "Rollback for REQUIRED")
37+
@TransactionConfiguration(defaultRollback = true)
38+
public class RollbackForRequiredEjbTxDaoTestNGTests extends CommitForRequiredEjbTxDaoTestNGTests {
39+
40+
/**
41+
* Redeclared to ensure test method execution order. Simply delegates to super.
42+
*/
43+
@Test
44+
@Override
45+
public void test1InitialState() {
46+
super.test1InitialState();
47+
}
48+
49+
/**
50+
* Redeclared to ensure test method execution order. Simply delegates to super.
51+
*/
52+
@Test(dependsOnMethods = "test1InitialState")
53+
@Override
54+
public void test2IncrementCount1() {
55+
super.test2IncrementCount1();
56+
}
57+
58+
/**
59+
* Overrides parent implementation in order to change expectations to align with
60+
* behavior associated with "required" transactions on repositories/DAOs and
61+
* default rollback semantics for transactions managed by the TestContext
62+
* framework.
63+
*/
64+
@Test(dependsOnMethods = "test2IncrementCount1")
65+
@Override
66+
public void test3IncrementCount2() {
67+
int count = dao.getCount(TEST_NAME);
68+
// Expecting count=0 after test2IncrementCount1() since REQUIRED transactions
69+
// participate in the existing transaction (if present), which in this case is the
70+
// transaction managed by the TestContext framework which will be rolled back
71+
// after each test method.
72+
assertEquals("Expected count=0 after test2IncrementCount1().", 0, count);
73+
74+
count = dao.incrementCount(TEST_NAME);
75+
assertEquals("Expected count=1 now.", 1, count);
76+
}
77+
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.testng.transaction.ejb;
18+
19+
import org.springframework.test.context.transaction.TransactionConfiguration;
20+
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
21+
import org.testng.annotations.Test;
22+
23+
/**
24+
* Extension of {@link CommitForRequiresNewEjbTxDaoTestNGTests} which sets the default
25+
* rollback semantics for the {@link TransactionalTestExecutionListener} to
26+
* {@code true}. The transaction managed by the TestContext framework will be
27+
* rolled back after each test method. Consequently, any work performed in
28+
* transactional methods that participate in the test-managed transaction will
29+
* be rolled back automatically. On the other hand, any work performed in
30+
* transactional methods that do <strong>not</strong> participate in the
31+
* test-managed transaction will not be affected by the rollback of the
32+
* test-managed transaction. For example, such work may in fact be committed
33+
* outside the scope of the test-managed transaction.
34+
*
35+
* @author Sam Brannen
36+
* @since 4.0.1
37+
*/
38+
@Test(suiteName = "Rollback for REQUIRES_NEW")
39+
@TransactionConfiguration(defaultRollback = true)
40+
public class RollbackForRequiresNewEjbTxDaoTestNGTests extends CommitForRequiresNewEjbTxDaoTestNGTests {
41+
42+
/* test methods in superclass */
43+
44+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
2+
3+
<suite name="EJB-TX-Package" verbose="1">
4+
<test name="Package">
5+
<packages>
6+
<package name="org.springframework.test.context.testng.transaction.ejb" />
7+
</packages>
8+
</test>
9+
</suite>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
2+
3+
<suite name="EJB-TX-Separate" verbose="1">
4+
<test name="RollbackForRequiredEjbTxDaoTestNGTests">
5+
<classes>
6+
<class name="org.springframework.test.context.testng.transaction.ejb.RollbackForRequiredEjbTxDaoTestNGTests" />
7+
</classes>
8+
</test>
9+
<test name="CommitForRequiredEjbTxDaoTestNGTests">
10+
<classes>
11+
<class name="org.springframework.test.context.testng.transaction.ejb.CommitForRequiredEjbTxDaoTestNGTests" />
12+
</classes>
13+
</test>
14+
<test name="CommitForRequiresNewEjbTxDaoTestNGTests">
15+
<classes>
16+
<class name="org.springframework.test.context.testng.transaction.ejb.CommitForRequiresNewEjbTxDaoTestNGTests" />
17+
</classes>
18+
</test>
19+
<test name="RollbackForRequiresNewEjbTxDaoTestNGTests">
20+
<classes>
21+
<class name="org.springframework.test.context.testng.transaction.ejb.RollbackForRequiresNewEjbTxDaoTestNGTests" />
22+
</classes>
23+
</test>
24+
</suite>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
2+
3+
<suite name="EJB-TX-Together" verbose="1">
4+
<test name="Together">
5+
<classes>
6+
<class name="org.springframework.test.context.testng.transaction.ejb.RollbackForRequiredEjbTxDaoTestNGTests" />
7+
<class name="org.springframework.test.context.testng.transaction.ejb.CommitForRequiredEjbTxDaoTestNGTests" />
8+
<class name="org.springframework.test.context.testng.transaction.ejb.CommitForRequiresNewEjbTxDaoTestNGTests" />
9+
<class name="org.springframework.test.context.testng.transaction.ejb.RollbackForRequiresNewEjbTxDaoTestNGTests" />
10+
</classes>
11+
</test>
12+
</suite>

0 commit comments

Comments
 (0)