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 class TestTransaction {
042
043        /**
044         * Determine whether a test-managed transaction is currently <em>active</em>.
045         * @return {@code true} if a test-managed transaction is currently active
046         * @see #start()
047         * @see #end()
048         */
049        public static boolean isActive() {
050                TransactionContext transactionContext = TransactionContextHolder.getCurrentTransactionContext();
051                if (transactionContext != null) {
052                        TransactionStatus transactionStatus = transactionContext.getTransactionStatus();
053                        return (transactionStatus != null && !transactionStatus.isCompleted());
054                }
055                return false;
056        }
057
058        /**
059         * Determine whether the current test-managed transaction has been
060         * {@linkplain #flagForRollback() flagged for rollback} or
061         * {@linkplain #flagForCommit() flagged for commit}.
062         * @return {@code true} if the current test-managed transaction is flagged
063         * to be rolled back; {@code false} if the current test-managed transaction
064         * is flagged to be committed
065         * @throws IllegalStateException if a transaction is not active for the
066         * current test
067         * @see #isActive()
068         * @see #flagForRollback()
069         * @see #flagForCommit()
070         */
071        public static boolean isFlaggedForRollback() {
072                return requireCurrentTransactionContext().isFlaggedForRollback();
073        }
074
075        /**
076         * Flag the current test-managed transaction for <em>rollback</em>.
077         * <p>Invoking this method will <em>not</em> end the current transaction.
078         * Rather, the value of this flag will be used to determine whether or not
079         * the current test-managed transaction should be rolled back or committed
080         * once it is {@linkplain #end ended}.
081         * @throws IllegalStateException if no transaction is active for the current test
082         * @see #isActive()
083         * @see #isFlaggedForRollback()
084         * @see #start()
085         * @see #end()
086         */
087        public static void flagForRollback() {
088                setFlaggedForRollback(true);
089        }
090
091        /**
092         * Flag the current test-managed transaction for <em>commit</em>.
093         * <p>Invoking this method will <em>not</em> end the current transaction.
094         * Rather, the value of this flag will be used to determine whether or not
095         * the current test-managed transaction should be rolled back or committed
096         * once it is {@linkplain #end ended}.
097         * @throws IllegalStateException if no transaction is active for the current test
098         * @see #isActive()
099         * @see #isFlaggedForRollback()
100         * @see #start()
101         * @see #end()
102         */
103        public static void flagForCommit() {
104                setFlaggedForRollback(false);
105        }
106
107        /**
108         * Start a new test-managed transaction.
109         * <p>Only call this method if {@link #end} has been called or if no
110         * transaction has been previously started.
111         * @throws IllegalStateException if the transaction context could not be
112         * retrieved or if a transaction is already active for the current test
113         * @see #isActive()
114         * @see #end()
115         */
116        public static void start() {
117                requireCurrentTransactionContext().startTransaction();
118        }
119
120        /**
121         * Immediately force a <em>commit</em> or <em>rollback</em> of the
122         * current test-managed transaction, according to the
123         * {@linkplain #isFlaggedForRollback rollback flag}.
124         * @throws IllegalStateException if the transaction context could not be
125         * retrieved or if a transaction is not active for the current test
126         * @see #isActive()
127         * @see #start()
128         */
129        public static void end() {
130                requireCurrentTransactionContext().endTransaction();
131        }
132
133
134        private static TransactionContext requireCurrentTransactionContext() {
135                TransactionContext txContext = TransactionContextHolder.getCurrentTransactionContext();
136                Assert.state(txContext != null, "TransactionContext is not active");
137                return txContext;
138        }
139
140        private static void setFlaggedForRollback(boolean flag) {
141                requireCurrentTransactionContext().setFlaggedForRollback(flag);
142        }
143
144}