001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.test.context.transaction;
018
019import org.springframework.test.context.TestExecutionListeners;
020import org.springframework.transaction.TransactionStatus;
021import org.springframework.util.Assert;
022
023/**
024 * {@code TestTransaction} provides a collection of static utility methods for
025 * programmatic interaction with <em>test-managed transactions</em> within
026 * <em>test</em> methods, <em>before</em> methods, and <em>after</em> methods.
027 *
028 * <p>Consult the javadocs for {@link TransactionalTestExecutionListener}
029 * for a detailed explanation of <em>test-managed transactions</em>.
030 *
031 * <p>Support for {@code TestTransaction} is automatically available whenever
032 * the {@code TransactionalTestExecutionListener} is enabled. Note that the
033 * {@code TransactionalTestExecutionListener} is typically enabled by default,
034 * but it can also be manually enabled via the
035 * {@link TestExecutionListeners @TestExecutionListeners} annotation.
036 *
037 * @author Sam Brannen
038 * @since 4.1
039 * @see TransactionalTestExecutionListener
040 */
041public final class TestTransaction {
042
043
044        private TestTransaction() {
045        }
046
047
048        /**
049         * Determine whether a test-managed transaction is currently <em>active</em>.
050         * @return {@code true} if a test-managed transaction is currently active
051         * @see #start()
052         * @see #end()
053         */
054        public static boolean isActive() {
055                TransactionContext transactionContext = TransactionContextHolder.getCurrentTransactionContext();
056                if (transactionContext != null) {
057                        TransactionStatus transactionStatus = transactionContext.getTransactionStatus();
058                        return (transactionStatus != null && !transactionStatus.isCompleted());
059                }
060                return false;
061        }
062
063        /**
064         * Determine whether the current test-managed transaction has been
065         * {@linkplain #flagForRollback() flagged for rollback} or
066         * {@linkplain #flagForCommit() flagged for commit}.
067         * @return {@code true} if the current test-managed transaction is flagged
068         * to be rolled back; {@code false} if the current test-managed transaction
069         * is flagged to be committed
070         * @throws IllegalStateException if a transaction is not active for the
071         * current test
072         * @see #isActive()
073         * @see #flagForRollback()
074         * @see #flagForCommit()
075         */
076        public static boolean isFlaggedForRollback() {
077                return requireCurrentTransactionContext().isFlaggedForRollback();
078        }
079
080        /**
081         * Flag the current test-managed transaction for <em>rollback</em>.
082         * <p>Invoking this method will <em>not</em> end the current transaction.
083         * Rather, the value of this flag will be used to determine whether or not
084         * the current test-managed transaction should be rolled back or committed
085         * once it is {@linkplain #end ended}.
086         * @throws IllegalStateException if no transaction is active for the current test
087         * @see #isActive()
088         * @see #isFlaggedForRollback()
089         * @see #start()
090         * @see #end()
091         */
092        public static void flagForRollback() {
093                setFlaggedForRollback(true);
094        }
095
096        /**
097         * Flag the current test-managed transaction for <em>commit</em>.
098         * <p>Invoking this method will <em>not</em> end the current transaction.
099         * Rather, the value of this flag will be used to determine whether or not
100         * the current test-managed transaction should be rolled back or committed
101         * once it is {@linkplain #end ended}.
102         * @throws IllegalStateException if no transaction is active for the current test
103         * @see #isActive()
104         * @see #isFlaggedForRollback()
105         * @see #start()
106         * @see #end()
107         */
108        public static void flagForCommit() {
109                setFlaggedForRollback(false);
110        }
111
112        /**
113         * Start a new test-managed transaction.
114         * <p>Only call this method if {@link #end} has been called or if no
115         * transaction has been previously started.
116         * @throws IllegalStateException if the transaction context could not be
117         * retrieved or if a transaction is already active for the current test
118         * @see #isActive()
119         * @see #end()
120         */
121        public static void start() {
122                requireCurrentTransactionContext().startTransaction();
123        }
124
125        /**
126         * Immediately force a <em>commit</em> or <em>rollback</em> of the
127         * current test-managed transaction, according to the
128         * {@linkplain #isFlaggedForRollback rollback flag}.
129         * @throws IllegalStateException if the transaction context could not be
130         * retrieved or if a transaction is not active for the current test
131         * @see #isActive()
132         * @see #start()
133         */
134        public static void end() {
135                requireCurrentTransactionContext().endTransaction();
136        }
137
138
139        private static TransactionContext requireCurrentTransactionContext() {
140                TransactionContext txContext = TransactionContextHolder.getCurrentTransactionContext();
141                Assert.state(txContext != null, "TransactionContext is not active");
142                return txContext;
143        }
144
145        private static void setFlaggedForRollback(boolean flag) {
146                requireCurrentTransactionContext().setFlaggedForRollback(flag);
147        }
148
149}