From c0eafa9ea15676cf5f4c64b1c6edf2646771059e Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 15 Jan 2014 15:53:23 +0100 Subject: [PATCH] Introduce EJB-based transactional tests in the TCF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- build.gradle | 2 + .../ejb/AbstractEjbTxDaoTestNGTests.java | 85 ++++++++++++++++++ .../CommitForRequiredEjbTxDaoTestNGTests.java | 41 +++++++++ ...mmitForRequiresNewEjbTxDaoTestNGTests.java | 41 +++++++++ ...ollbackForRequiredEjbTxDaoTestNGTests.java | 78 ++++++++++++++++ ...backForRequiresNewEjbTxDaoTestNGTests.java | 44 ++++++++++ .../testng/transaction/ejb/testng-package.xml | 9 ++ .../transaction/ejb/testng-test-separate.xml | 24 +++++ .../transaction/ejb/testng-test-together.xml | 12 +++ .../ejb/AbstractEjbTxDaoTests.java | 88 +++++++++++++++++++ .../ejb/CommitForRequiredEjbTxDaoTests.java | 39 ++++++++ .../CommitForRequiresNewEjbTxDaoTests.java | 39 ++++++++ .../ejb/RollbackForRequiredEjbTxDaoTests.java | 80 +++++++++++++++++ .../RollbackForRequiresNewEjbTxDaoTests.java | 42 +++++++++ .../ejb/dao/AbstractEjbTxTestEntityDao.java | 63 +++++++++++++ .../ejb/dao/RequiredEjbTxTestEntityDao.java | 50 +++++++++++ .../dao/RequiresNewEjbTxTestEntityDao.java | 50 +++++++++++ .../transaction/ejb/dao/TestEntityDao.java | 32 +++++++ .../transaction/ejb/model/TestEntity.java | 68 ++++++++++++++ .../context/transaction/ejb/common-config.xml | 33 +++++++ .../transaction/ejb/required-tx-config.xml | 9 ++ .../ejb/requires-new-tx-config.xml | 9 ++ 22 files changed, 938 insertions(+) create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/AbstractEjbTxDaoTestNGTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/CommitForRequiredEjbTxDaoTestNGTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/CommitForRequiresNewEjbTxDaoTestNGTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/RollbackForRequiredEjbTxDaoTestNGTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/RollbackForRequiresNewEjbTxDaoTestNGTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-package.xml create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-test-separate.xml create mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-test-together.xml create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/AbstractEjbTxDaoTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/CommitForRequiredEjbTxDaoTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/CommitForRequiresNewEjbTxDaoTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/RollbackForRequiredEjbTxDaoTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/RollbackForRequiresNewEjbTxDaoTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/AbstractEjbTxTestEntityDao.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/RequiredEjbTxTestEntityDao.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/RequiresNewEjbTxTestEntityDao.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/TestEntityDao.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/transaction/ejb/model/TestEntity.java create mode 100644 spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/common-config.xml create mode 100644 spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/required-tx-config.xml create mode 100644 spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/requires-new-tx-config.xml diff --git a/build.gradle b/build.gradle index 87aa765f46e2..13f6049d565d 100644 --- a/build.gradle +++ b/build.gradle @@ -804,7 +804,9 @@ project("spring-test") { testCompile(project(":spring-context-support")) testCompile(project(":spring-oxm")) testCompile(project(":spring-webmvc-tiles3")) + testCompile("javax.ejb:ejb-api:3.0") testCompile("org.hibernate:hibernate-core:${hibernate3Version}") + testCompile("org.hibernate:hibernate-entitymanager:${hibernate3Version}") testCompile "org.slf4j:slf4j-jcl:${slf4jVersion}" testCompile("org.hsqldb:hsqldb:${hsqldbVersion}") testCompile("org.hibernate:hibernate-validator:4.3.0.Final") diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/AbstractEjbTxDaoTestNGTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/AbstractEjbTxDaoTestNGTests.java new file mode 100644 index 000000000000..71686cc83457 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/AbstractEjbTxDaoTestNGTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.testng.transaction.ejb; + +import javax.ejb.EJB; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; +import org.springframework.test.context.transaction.ejb.dao.TestEntityDao; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import static org.testng.AssertJUnit.*; + +/** + * Abstract base class for all TestNG-based tests involving EJB transaction + * support in the TestContext framework. + * + * @author Sam Brannen + * @author Xavier Detant + * @since 4.0.1 + */ +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public abstract class AbstractEjbTxDaoTestNGTests extends AbstractTransactionalTestNGSpringContextTests { + + protected static final String TEST_NAME = "test-name"; + + @EJB + protected TestEntityDao dao; + + @PersistenceContext + protected EntityManager em; + + + @Test + public void test1InitialState() { + int count = dao.getCount(TEST_NAME); + assertEquals("New TestEntity should have count=0.", 0, count); + } + + @Test(dependsOnMethods = "test1InitialState") + public void test2IncrementCount1() { + int count = dao.incrementCount(TEST_NAME); + assertEquals("Expected count=1 after first increment.", 1, count); + } + + /** + * The default implementation of this method assumes that the transaction + * for {@link #test2IncrementCount1()} was committed. Therefore, it is + * expected that the previous increment has been persisted in the database. + */ + @Test(dependsOnMethods = "test2IncrementCount1") + public void test3IncrementCount2() { + int count = dao.getCount(TEST_NAME); + assertEquals("Expected count=1 after test2IncrementCount1().", 1, count); + + count = dao.incrementCount(TEST_NAME); + assertEquals("Expected count=2 now.", 2, count); + } + + @AfterMethod(alwaysRun = true) + public void synchronizePersistenceContext() { + em.flush(); + } + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/CommitForRequiredEjbTxDaoTestNGTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/CommitForRequiredEjbTxDaoTestNGTests.java new file mode 100644 index 000000000000..851e674b7b05 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/CommitForRequiredEjbTxDaoTestNGTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.testng.transaction.ejb; + +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.transaction.ejb.dao.RequiredEjbTxTestEntityDao; +import org.testng.annotations.Test; + +/** + * Concrete subclass of {@link AbstractEjbTxDaoTestNGTests} which uses the + * {@link RequiredEjbTxTestEntityDao} and sets the default rollback semantics + * for the {@link TransactionalTestExecutionListener} to {@code false} (i.e., + * commit). + * + * @author Sam Brannen + * @since 4.0.1 + */ +@Test(suiteName = "Commit for REQUIRED") +@ContextConfiguration("/org/springframework/test/context/transaction/ejb/required-tx-config.xml") +@TransactionConfiguration(defaultRollback = false) +public class CommitForRequiredEjbTxDaoTestNGTests extends AbstractEjbTxDaoTestNGTests { + + /* test methods in superclass */ + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/CommitForRequiresNewEjbTxDaoTestNGTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/CommitForRequiresNewEjbTxDaoTestNGTests.java new file mode 100644 index 000000000000..0310c882dedc --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/CommitForRequiresNewEjbTxDaoTestNGTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.testng.transaction.ejb; + +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.transaction.ejb.dao.RequiresNewEjbTxTestEntityDao; +import org.testng.annotations.Test; + +/** + * Concrete subclass of {@link AbstractEjbTxDaoTestNGTests} which uses the + * {@link RequiresNewEjbTxTestEntityDao} and sets the default rollback semantics + * for the {@link TransactionalTestExecutionListener} to {@code false} (i.e., + * commit). + * + * @author Sam Brannen + * @since 4.0.1 + */ +@Test(suiteName = "Commit for REQUIRES_NEW") +@ContextConfiguration("/org/springframework/test/context/transaction/ejb/requires-new-tx-config.xml") +@TransactionConfiguration(defaultRollback = false) +public class CommitForRequiresNewEjbTxDaoTestNGTests extends AbstractEjbTxDaoTestNGTests { + + /* test methods in superclass */ + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/RollbackForRequiredEjbTxDaoTestNGTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/RollbackForRequiredEjbTxDaoTestNGTests.java new file mode 100644 index 000000000000..cf00e5d5de95 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/RollbackForRequiredEjbTxDaoTestNGTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.testng.transaction.ejb; + +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.testng.annotations.Test; + +import static org.testng.AssertJUnit.*; + +/** + * Extension of {@link CommitForRequiredEjbTxDaoTestNGTests} which sets the default + * rollback semantics for the {@link TransactionalTestExecutionListener} to + * {@code true}. The transaction managed by the TestContext framework will be + * rolled back after each test method. Consequently, any work performed in + * transactional methods that participate in the test-managed transaction will + * be rolled back automatically. + * + * @author Sam Brannen + * @since 4.0.1 + */ +@Test(suiteName = "Rollback for REQUIRED") +@TransactionConfiguration(defaultRollback = true) +public class RollbackForRequiredEjbTxDaoTestNGTests extends CommitForRequiredEjbTxDaoTestNGTests { + + /** + * Redeclared to ensure test method execution order. Simply delegates to super. + */ + @Test + @Override + public void test1InitialState() { + super.test1InitialState(); + } + + /** + * Redeclared to ensure test method execution order. Simply delegates to super. + */ + @Test(dependsOnMethods = "test1InitialState") + @Override + public void test2IncrementCount1() { + super.test2IncrementCount1(); + } + + /** + * Overrides parent implementation in order to change expectations to align with + * behavior associated with "required" transactions on repositories/DAOs and + * default rollback semantics for transactions managed by the TestContext + * framework. + */ + @Test(dependsOnMethods = "test2IncrementCount1") + @Override + public void test3IncrementCount2() { + int count = dao.getCount(TEST_NAME); + // Expecting count=0 after test2IncrementCount1() since REQUIRED transactions + // participate in the existing transaction (if present), which in this case is the + // transaction managed by the TestContext framework which will be rolled back + // after each test method. + assertEquals("Expected count=0 after test2IncrementCount1().", 0, count); + + count = dao.incrementCount(TEST_NAME); + assertEquals("Expected count=1 now.", 1, count); + } + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/RollbackForRequiresNewEjbTxDaoTestNGTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/RollbackForRequiresNewEjbTxDaoTestNGTests.java new file mode 100644 index 000000000000..fa5b62afca12 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/RollbackForRequiresNewEjbTxDaoTestNGTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.testng.transaction.ejb; + +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.testng.annotations.Test; + +/** + * Extension of {@link CommitForRequiresNewEjbTxDaoTestNGTests} which sets the default + * rollback semantics for the {@link TransactionalTestExecutionListener} to + * {@code true}. The transaction managed by the TestContext framework will be + * rolled back after each test method. Consequently, any work performed in + * transactional methods that participate in the test-managed transaction will + * be rolled back automatically. On the other hand, any work performed in + * transactional methods that do not participate in the + * test-managed transaction will not be affected by the rollback of the + * test-managed transaction. For example, such work may in fact be committed + * outside the scope of the test-managed transaction. + * + * @author Sam Brannen + * @since 4.0.1 + */ +@Test(suiteName = "Rollback for REQUIRES_NEW") +@TransactionConfiguration(defaultRollback = true) +public class RollbackForRequiresNewEjbTxDaoTestNGTests extends CommitForRequiresNewEjbTxDaoTestNGTests { + + /* test methods in superclass */ + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-package.xml b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-package.xml new file mode 100644 index 000000000000..c3567d58e588 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-package.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-test-separate.xml b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-test-separate.xml new file mode 100644 index 000000000000..fc95db9b8b7b --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-test-separate.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-test-together.xml b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-test-together.xml new file mode 100644 index 000000000000..e3c4e0078aa8 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/testng/transaction/ejb/testng-test-together.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/AbstractEjbTxDaoTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/AbstractEjbTxDaoTests.java new file mode 100644 index 000000000000..89fbd7bec5b9 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/AbstractEjbTxDaoTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb; + +import javax.ejb.EJB; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.junit.After; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; +import org.springframework.test.context.transaction.ejb.dao.TestEntityDao; + +import static org.junit.Assert.*; + +/** + * Abstract base class for all tests involving EJB transaction support in the + * TestContext framework. + * + * @author Sam Brannen + * @author Xavier Detant + * @since 4.0.1 + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public abstract class AbstractEjbTxDaoTests extends AbstractTransactionalJUnit4SpringContextTests { + + protected static final String TEST_NAME = "test-name"; + + @EJB + protected TestEntityDao dao; + + @PersistenceContext + protected EntityManager em; + + + @Test + public void test1InitialState() { + int count = dao.getCount(TEST_NAME); + assertEquals("New TestEntity should have count=0.", 0, count); + } + + @Test + public void test2IncrementCount1() { + int count = dao.incrementCount(TEST_NAME); + assertEquals("Expected count=1 after first increment.", 1, count); + } + + /** + * The default implementation of this method assumes that the transaction + * for {@link #test2IncrementCount1()} was committed. Therefore, it is + * expected that the previous increment has been persisted in the database. + */ + @Test + public void test3IncrementCount2() { + int count = dao.getCount(TEST_NAME); + assertEquals("Expected count=1 after test2IncrementCount1().", 1, count); + + count = dao.incrementCount(TEST_NAME); + assertEquals("Expected count=2 now.", 2, count); + } + + @After + public void synchronizePersistenceContext() { + em.flush(); + } + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/CommitForRequiredEjbTxDaoTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/CommitForRequiredEjbTxDaoTests.java new file mode 100644 index 000000000000..74d8067d1a27 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/CommitForRequiredEjbTxDaoTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb; + +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.transaction.ejb.dao.RequiredEjbTxTestEntityDao; + +/** + * Concrete subclass of {@link AbstractEjbTxDaoTests} which uses the + * {@link RequiredEjbTxTestEntityDao} and sets the default rollback semantics + * for the {@link TransactionalTestExecutionListener} to {@code false} (i.e., + * commit). + * + * @author Sam Brannen + * @since 4.0.1 + */ +@ContextConfiguration("required-tx-config.xml") +@TransactionConfiguration(defaultRollback = false) +public class CommitForRequiredEjbTxDaoTests extends AbstractEjbTxDaoTests { + + /* test methods in superclass */ + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/CommitForRequiresNewEjbTxDaoTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/CommitForRequiresNewEjbTxDaoTests.java new file mode 100644 index 000000000000..4651b119dec2 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/CommitForRequiresNewEjbTxDaoTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb; + +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.test.context.transaction.ejb.dao.RequiresNewEjbTxTestEntityDao; + +/** + * Concrete subclass of {@link AbstractEjbTxDaoTests} which uses the + * {@link RequiresNewEjbTxTestEntityDao} and sets the default rollback semantics + * for the {@link TransactionalTestExecutionListener} to {@code false} (i.e., + * commit). + * + * @author Sam Brannen + * @since 4.0.1 + */ +@ContextConfiguration("requires-new-tx-config.xml") +@TransactionConfiguration(defaultRollback = false) +public class CommitForRequiresNewEjbTxDaoTests extends AbstractEjbTxDaoTests { + + /* test methods in superclass */ + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/RollbackForRequiredEjbTxDaoTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/RollbackForRequiredEjbTxDaoTests.java new file mode 100644 index 000000000000..491afebeaf8a --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/RollbackForRequiredEjbTxDaoTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; + +import static org.junit.Assert.*; + +/** + * Extension of {@link CommitForRequiredEjbTxDaoTests} which sets the default + * rollback semantics for the {@link TransactionalTestExecutionListener} to + * {@code true}. The transaction managed by the TestContext framework will be + * rolled back after each test method. Consequently, any work performed in + * transactional methods that participate in the test-managed transaction will + * be rolled back automatically. + * + * @author Sam Brannen + * @since 4.0.1 + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@TransactionConfiguration(defaultRollback = true) +public class RollbackForRequiredEjbTxDaoTests extends CommitForRequiredEjbTxDaoTests { + + /** + * Redeclared to ensure test method execution order. Simply delegates to super. + */ + @Test + @Override + public void test1InitialState() { + super.test1InitialState(); + } + + /** + * Redeclared to ensure test method execution order. Simply delegates to super. + */ + @Test + @Override + public void test2IncrementCount1() { + super.test2IncrementCount1(); + } + + /** + * Overrides parent implementation in order to change expectations to align with + * behavior associated with "required" transactions on repositories/DAOs and + * default rollback semantics for transactions managed by the TestContext + * framework. + */ + @Test + @Override + public void test3IncrementCount2() { + int count = dao.getCount(TEST_NAME); + // Expecting count=0 after test2IncrementCount1() since REQUIRED transactions + // participate in the existing transaction (if present), which in this case is the + // transaction managed by the TestContext framework which will be rolled back + // after each test method. + assertEquals("Expected count=0 after test2IncrementCount1().", 0, count); + + count = dao.incrementCount(TEST_NAME); + assertEquals("Expected count=1 now.", 1, count); + } + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/RollbackForRequiresNewEjbTxDaoTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/RollbackForRequiresNewEjbTxDaoTests.java new file mode 100644 index 000000000000..ec3147258c45 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/RollbackForRequiresNewEjbTxDaoTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb; + +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; + +/** + * Extension of {@link CommitForRequiresNewEjbTxDaoTests} which sets the default + * rollback semantics for the {@link TransactionalTestExecutionListener} to + * {@code true}. The transaction managed by the TestContext framework will be + * rolled back after each test method. Consequently, any work performed in + * transactional methods that participate in the test-managed transaction will + * be rolled back automatically. On the other hand, any work performed in + * transactional methods that do not participate in the + * test-managed transaction will not be affected by the rollback of the + * test-managed transaction. For example, such work may in fact be committed + * outside the scope of the test-managed transaction. + * + * @author Sam Brannen + * @since 4.0.1 + */ +@TransactionConfiguration(defaultRollback = true) +public class RollbackForRequiresNewEjbTxDaoTests extends CommitForRequiresNewEjbTxDaoTests { + + /* test methods in superclass */ + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/AbstractEjbTxTestEntityDao.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/AbstractEjbTxTestEntityDao.java new file mode 100644 index 000000000000..24fc4cac1f16 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/AbstractEjbTxTestEntityDao.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb.dao; + +import javax.ejb.TransactionAttribute; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.springframework.test.context.transaction.ejb.model.TestEntity; + +/** + * Abstract base class for EJB implementations of {@link TestEntityDao} which + * declare transaction semantics for {@link #incrementCount(String)} via + * {@link TransactionAttribute}. + * + * @author Sam Brannen + * @author Xavier Detant + * @since 4.0.1 + * @see RequiredEjbTxTestEntityDao + * @see RequiresNewEjbTxTestEntityDao + */ +public abstract class AbstractEjbTxTestEntityDao implements TestEntityDao { + + @PersistenceContext + protected EntityManager entityManager; + + + protected final TestEntity getTestEntity(String name) { + TestEntity te = entityManager.find(TestEntity.class, name); + if (te == null) { + te = new TestEntity(name, 0); + entityManager.persist(te); + } + return te; + } + + protected final int getCountInternal(String name) { + return getTestEntity(name).getCount(); + } + + protected final int incrementCountInternal(String name) { + TestEntity te = getTestEntity(name); + int count = te.getCount(); + count++; + te.setCount(count); + return count; + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/RequiredEjbTxTestEntityDao.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/RequiredEjbTxTestEntityDao.java new file mode 100644 index 000000000000..340e39a27379 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/RequiredEjbTxTestEntityDao.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb.dao; + +import javax.ejb.Local; +import javax.ejb.Stateless; +import javax.ejb.TransactionAttribute; +import javax.ejb.TransactionAttributeType; + +/** + * EJB implementation of {@link TestEntityDao} which declares transaction + * semantics for {@link #incrementCount(String)} with + * {@link TransactionAttributeType#REQUIRED}. + * + * @author Sam Brannen + * @author Xavier Detant + * @since 4.0.1 + * @see RequiresNewEjbTxTestEntityDao + */ +@Stateless +@Local(TestEntityDao.class) +@TransactionAttribute(TransactionAttributeType.MANDATORY) +public class RequiredEjbTxTestEntityDao extends AbstractEjbTxTestEntityDao { + + @Override + public int getCount(String name) { + return super.getCountInternal(name); + } + + @TransactionAttribute(TransactionAttributeType.REQUIRED) + @Override + public int incrementCount(String name) { + return super.incrementCountInternal(name); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/RequiresNewEjbTxTestEntityDao.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/RequiresNewEjbTxTestEntityDao.java new file mode 100644 index 000000000000..68fdcd3938e2 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/RequiresNewEjbTxTestEntityDao.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb.dao; + +import javax.ejb.Local; +import javax.ejb.Stateless; +import javax.ejb.TransactionAttribute; +import javax.ejb.TransactionAttributeType; + +/** + * EJB implementation of {@link TestEntityDao} which declares transaction + * semantics for {@link #incrementCount(String)} with + * {@link TransactionAttributeType#REQUIRES_NEW}. + * + * @author Sam Brannen + * @author Xavier Detant + * @since 4.0.1 + * @see RequiredEjbTxTestEntityDao + */ +@Stateless +@Local(TestEntityDao.class) +@TransactionAttribute(TransactionAttributeType.MANDATORY) +public class RequiresNewEjbTxTestEntityDao extends AbstractEjbTxTestEntityDao { + + @Override + public int getCount(String name) { + return super.getCountInternal(name); + } + + @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) + @Override + public int incrementCount(String name) { + return super.incrementCountInternal(name); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/TestEntityDao.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/TestEntityDao.java new file mode 100644 index 000000000000..361f0ec63b96 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/dao/TestEntityDao.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb.dao; + +/** + * Test DAO for EJB transaction support in the TestContext framework. + * + * @author Xavier Detant + * @author Sam Brannen + * @since 4.0.1 + */ +public interface TestEntityDao { + + int getCount(String name); + + int incrementCount(String name); + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/model/TestEntity.java b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/model/TestEntity.java new file mode 100644 index 000000000000..9d2e82b9d4c1 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/ejb/model/TestEntity.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.ejb.model; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Test entity for EJB transaction support in the TestContext framework. + * + * @author Xavier Detant + * @author Sam Brannen + * @since 4.0.1 + */ +@Entity +@Table(name = TestEntity.TABLE_NAME) +public class TestEntity { + + public static final String TABLE_NAME = "TEST_ENTITY"; + + @Id + @Column(name = "TE_NAME", nullable = false) + private String name; + + @Column(name = "TE_COUNT", nullable = false) + private int count; + + + public TestEntity() { + } + + public TestEntity(String name, int count) { + this.name = name; + this.count = count; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCount() { + return this.count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/common-config.xml b/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/common-config.xml new file mode 100644 index 000000000000..b801b600d13b --- /dev/null +++ b/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/common-config.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/required-tx-config.xml b/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/required-tx-config.xml new file mode 100644 index 000000000000..a159597bc6c1 --- /dev/null +++ b/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/required-tx-config.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/requires-new-tx-config.xml b/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/requires-new-tx-config.xml new file mode 100644 index 000000000000..a061be1db578 --- /dev/null +++ b/spring-test/src/test/resources/org/springframework/test/context/transaction/ejb/requires-new-tx-config.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file