diff --git a/src/main/java/io/ebean/ForTests.java b/src/main/java/io/ebean/ForTests.java
new file mode 100644
index 000000000..d1b3d89b2
--- /dev/null
+++ b/src/main/java/io/ebean/ForTests.java
@@ -0,0 +1,113 @@
+package io.ebean;
+
+import io.ebeaninternal.api.HelpScopeTrans;
+
+/**
+ * Helper methods for testing.
+ */
+public class ForTests {
+
+ /**
+ * Enable or disable @Transactional methods.
+ *
+ * This is intended for testing purposes such that tests
+ * on code with {@code @Transactional} methods don't actually
+ * start or complete transactions.
+ *
+ *
+ * @param enable Set false to disable {@code @Transactional} methods
+ */
+ public static void enableTransactional(boolean enable) {
+ HelpScopeTrans.setEnabled(enable);
+ }
+
+ /**
+ * Run the closure with @Transactional methods
+ * effectively disabled (they won't create/commit transactions).
+ */
+ public static void noTransactional(Runnable run) {
+ try {
+ enableTransactional(false);
+ run.run();
+ } finally {
+ enableTransactional(true);
+ }
+ }
+
+ /**
+ * All transactions started in the closure are effectively rolled back.
+ *
+ * This creates a wrapping transaction that uses {@link Transaction#setNestedUseSavepoint()}.
+ * All nested transactions are created as savepoints. On completion the wrapping
+ * transaction is rolled back.
+ *
+ *
+ * @param run Closure that runs such that all the transactions are rolled back.
+ */
+ public static void rollbackAll(Runnable run) {
+
+ try (Transaction transaction = DB.beginTransaction()) {
+ transaction.setNestedUseSavepoint();
+ run.run();
+
+ transaction.rollback();
+ }
+ }
+
+ /**
+ * Create and return a RollbackAll which should be closed at the end of the test(s).
+ *
+ * In tests for @Before we create the rollbackAll and on
+ * @After we close() it effectively rolling
+ * back all changes made during test execution.
+ *
+ *
+ * {@code
+ *
+ * private ForTests.RollbackAll rollbackAll;
+ *
+ * @Before
+ * public void before() {
+ * rollbackAll = ForTests.createRollbackAll();
+ * }
+ *
+ * @After
+ * public void after() {
+ * rollbackAll.close();
+ * }
+ *
+ * ... tests execute and everything is rolled back
+ *
+ *
+ * }
+ */
+ public static RollbackAll createRollbackAll() {
+
+ final Transaction transaction = DB.beginTransaction();
+ transaction.setNestedUseSavepoint();
+ return new RollbackAll(transaction);
+ }
+
+ /**
+ * A wrapping transaction used in test code to rollback all changes.
+ *
+ * We must ensure that close() is called.
+ *
+ */
+ public static class RollbackAll implements AutoCloseable {
+
+ private final Transaction transaction;
+
+ private RollbackAll(Transaction transaction) {
+ this.transaction = transaction;
+ }
+
+ /**
+ * Rollback the wrapping transaction.
+ */
+ @Override
+ public void close() {
+ transaction.rollback();
+ }
+ }
+}
diff --git a/src/test/java/org/tests/ForTestsBeforeAfterTest.java b/src/test/java/org/tests/ForTestsBeforeAfterTest.java
new file mode 100644
index 000000000..f1832b4be
--- /dev/null
+++ b/src/test/java/org/tests/ForTestsBeforeAfterTest.java
@@ -0,0 +1,49 @@
+package org.tests;
+
+import io.ebean.DB;
+import io.ebean.ForTests;
+import io.ebean.annotation.Transactional;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.tests.basic.type.BSimpleWithGen;
+
+import static org.assertj.core.api.StrictAssertions.assertThat;
+
+public class ForTestsBeforeAfterTest {
+
+ private ForTests.RollbackAll rollbackAll;
+
+ @Before
+ public void before() {
+ rollbackAll = ForTests.createRollbackAll();
+ }
+
+ @After
+ public void after() {
+ rollbackAll.close();
+ assertThat(getCount()).isEqualTo(0);
+ }
+
+
+ @Test
+ public void createRollbackAll() {
+
+ doInsert();
+ assertThat(getCount()).isEqualTo(1);
+ }
+
+ private int getCount() {
+ return DB.find(BSimpleWithGen.class)
+ .where().eq("name", "ForTestsBeforeAfterTest")
+ .findCount();
+ }
+
+ @Transactional
+ private void doInsert() {
+
+ BSimpleWithGen bean = new BSimpleWithGen("ForTestsBeforeAfterTest");
+ DB.save(bean);
+ }
+
+}
diff --git a/src/test/java/org/tests/ForTestsTest.java b/src/test/java/org/tests/ForTestsTest.java
new file mode 100644
index 000000000..fb5052f82
--- /dev/null
+++ b/src/test/java/org/tests/ForTestsTest.java
@@ -0,0 +1,54 @@
+package org.tests;
+
+import io.ebean.DB;
+import io.ebean.ForTests;
+import io.ebean.Transaction;
+import io.ebean.annotation.Transactional;
+import org.junit.Test;
+import org.tests.basic.type.BSimpleWithGen;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ForTestsTest {
+
+ @Test
+ public void noTransactional_expect_noTransactionEnterExitCalled() {
+
+ ForTests.noTransactional(this::checkForTransactional);
+ }
+
+ @Test
+ public void enableTransactional() {
+
+ ForTests.enableTransactional(false);
+
+ checkForTransactional();
+
+ ForTests.enableTransactional(true);
+ }
+
+ @Transactional
+ private void checkForTransactional() {
+
+ final Transaction transaction = DB.currentTransaction();
+ assertThat(transaction).isNull();
+ }
+
+ @Test
+ public void rollbackTransactions() {
+
+ DB.find(BSimpleWithGen.class).delete();
+
+ ForTests.rollbackAll(this::doInsert);
+
+ final int count = DB.find(BSimpleWithGen.class).findCount();
+ assertThat(count).isEqualTo(0);
+ }
+
+ @Transactional
+ private void doInsert() {
+
+ BSimpleWithGen bean = new BSimpleWithGen("doInsert");
+ DB.save(bean);
+ }
+}