All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.test.AbstractTransactionalSpringContextTests Maven / Gradle / Ivy

There is a newer version: 5.3.39
Show newest version
/*
 * Copyright 2002-2007 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;

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 * Convenient base class for tests that should occur in a transaction, but normally
 * will roll the transaction back on the completion of each test.
 *
 * 

This is useful in a range of circumstances, allowing the following benefits: *

    *
  • Ability to delete or insert any data in the database, without affecting other tests *
  • Providing a transactional context for any code requiring a transaction *
  • Ability to write anything to the database without any need to clean up. *
* *

This class is typically very fast, compared to traditional setup/teardown scripts. * *

If data should be left in the database, call the {@link #setComplete()} * method in each test. The {@link #setDefaultRollback "defaultRollback"} property, * which defaults to "true", determines whether transactions will complete by default. * *

It is even possible to end the transaction early; for example, to verify lazy * loading behavior of an O/R mapping tool. (This is a valuable away to avoid * unexpected errors when testing a web UI, for example.) Simply call the * {@link #endTransaction()} method. Execution will then occur without a * transactional context. * *

The {@link #startNewTransaction()} method may be called after a call to * {@link #endTransaction()} if you wish to create a new transaction, quite * independent of the old transaction. The new transaction's default fate will be to * roll back, unless {@link #setComplete()} is called again during the scope of the * new transaction. Any number of transactions may be created and ended in this way. * The final transaction will automatically be rolled back when the test case is * torn down. * *

Transactional behavior requires a single bean in the context implementing the * {@link org.springframework.transaction.PlatformTransactionManager} interface. * This will be set by the superclass's Dependency Injection mechanism. * If using the superclass's Field Injection mechanism, the implementation should * be named "transactionManager". This mechanism allows the use of the * {@link AbstractDependencyInjectionSpringContextTests} superclass even * when there is more than one transaction manager in the context. * *

This base class can also be used without transaction management, if no * PlatformTransactionManager bean is found in the context provided. * Be careful about using this mode, as it allows the potential to permanently modify * data. This mode is available only if dependency checking is turned off in the * {@link AbstractDependencyInjectionSpringContextTests} superclass. The non-transactional * capability is provided to enable use of the same subclass in different environments. * * @author Rod Johnson * @author Juergen Hoeller * @since 1.1.1 */ public abstract class AbstractTransactionalSpringContextTests extends AbstractDependencyInjectionSpringContextTests { /** The transaction manager to use */ protected PlatformTransactionManager transactionManager; /** Should we roll back by default? */ private boolean defaultRollback = true; /** Should we commit the current transaction? */ private boolean complete = false; /** Number of transactions started */ private int transactionsStarted = 0; /** * Transaction definition used by this test class: by default, a plain * DefaultTransactionDefinition. Subclasses can change this to cause different behavior. */ protected TransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); /** * TransactionStatus for this test. Typical subclasses won't need to use it. */ protected TransactionStatus transactionStatus; /** * Default constructor for AbstractTransactionalSpringContextTests. */ public AbstractTransactionalSpringContextTests() { } /** * Constructor for AbstractTransactionalSpringContextTests with a JUnit name. */ public AbstractTransactionalSpringContextTests(String name) { super(name); } /** * Specify the transaction manager to use. No transaction management will be available * if this is not set. Populated through dependency injection by the superclass. *

This mode works only if dependency checking is turned off in the * {@link AbstractDependencyInjectionSpringContextTests} superclass. */ public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } /** * Subclasses can set this value in their constructor to change * default, which is always to roll the transaction back. */ public void setDefaultRollback(boolean defaultRollback) { this.defaultRollback = defaultRollback; } /** * Call this method in an overridden {@link #runBare()} method to * prevent transactional execution. */ protected void preventTransaction() { this.transactionDefinition = null; } /** * Call this method in an overridden {@link #runBare()} method to * override the transaction attributes that will be used, so that * {@link #setUp()} and {@link #tearDown()} behavior is modified. * @param customDefinition the custom transaction definition */ protected void setTransactionDefinition(TransactionDefinition customDefinition) { this.transactionDefinition = customDefinition; } /** * This implementation creates a transaction before test execution. *

Override {@link #onSetUpBeforeTransaction()} and/or * {@link #onSetUpInTransaction()} to add custom set-up behavior * for transactional execution. Alternatively, override this method * for general set-up behavior, calling super.onSetUp() * as part of your method implementation. * @throws Exception simply let any exception propagate * @see #onTearDown() */ protected void onSetUp() throws Exception { this.complete = !this.defaultRollback; if (this.transactionManager == null) { logger.info("No transaction manager set: test will NOT run within a transaction"); } else if (this.transactionDefinition == null) { logger.info("No transaction definition set: test will NOT run within a transaction"); } else { onSetUpBeforeTransaction(); startNewTransaction(); try { onSetUpInTransaction(); } catch (Exception ex) { endTransaction(); throw ex; } } } /** * Subclasses can override this method to perform any setup operations, * such as populating a database table, before the transaction * created by this class. Only invoked if there is a transaction: * that is, if {@link #preventTransaction()} has not been invoked in * an overridden {@link #runTest()} method. * @throws Exception simply let any exception propagate */ protected void onSetUpBeforeTransaction() throws Exception { } /** * Subclasses can override this method to perform any setup operations, * such as populating a database table, within the transaction * created by this class. *

NB: Not called if there is no transaction management, due to no * transaction manager being provided in the context. *

If any {@link Throwable} is thrown, the transaction that has been started * prior to the execution of this method will be {@link #endTransaction() ended} * (or rather an attempt will be made to {@link #endTransaction() end it gracefully}); * The offending {@link Throwable} will then be rethrown. * @throws Exception simply let any exception propagate */ protected void onSetUpInTransaction() throws Exception { } /** * This implementation ends the transaction after test execution. *

Override {@link #onTearDownInTransaction()} and/or * {@link #onTearDownAfterTransaction()} to add custom tear-down behavior * for transactional execution. Alternatively, override this method for * general tear-down behavior, calling super.onTearDown() * as part of your method implementation. *

Note that {@link #onTearDownInTransaction()} will only be called * if a transaction is still active at the time of the test shutdown. * In particular, it will not be called if the transaction has * been completed with an explicit {@link #endTransaction()} call before. * @throws Exception simply let any exception propagate * @see #onSetUp() */ protected void onTearDown() throws Exception { // Call onTearDownInTransaction and end transaction if the transaction is still active. if (this.transactionStatus != null && !this.transactionStatus.isCompleted()) { try { onTearDownInTransaction(); } finally { endTransaction(); } } // Call onTearDownAfterTransaction if there was at least one transaction, // even if it has been completed early through an endTransaction() call. if (this.transactionsStarted > 0) { onTearDownAfterTransaction(); } } /** * Subclasses can override this method to run invariant tests here. * The transaction is still active at this point, so any changes * made in the transaction will still be visible. However, there is no need * to clean up the database, as a rollback will follow automatically. *

NB: Not called if there is no actual transaction, for example * due to no transaction manager being provided in the application context. * @throws Exception simply let any exception propagate */ protected void onTearDownInTransaction() throws Exception { } /** * Subclasses can override this method to perform cleanup after a transaction * here. At this point, the transaction is not active anymore. * @throws Exception simply let any exception propagate */ protected void onTearDownAfterTransaction() throws Exception { } /** * Cause the transaction to commit for this test method, * even if default is set to rollback. * @throws IllegalStateException if the operation cannot be set to * complete as no transaction manager was provided */ protected void setComplete() { if (this.transactionManager == null) { throw new IllegalStateException("No transaction manager set"); } this.complete = true; } /** * Immediately force a commit or rollback of the transaction, * according to the complete flag. *

Can be used to explicitly let the transaction end early, * for example to check whether lazy associations of persistent objects * work outside of a transaction (that is, have been initialized properly). * @see #setComplete() */ protected void endTransaction() { if (this.transactionStatus != null) { try { if (!this.complete) { this.transactionManager.rollback(this.transactionStatus); logger.info("Rolled back transaction after test execution"); } else { this.transactionManager.commit(this.transactionStatus); logger.info("Committed transaction after test execution"); } } finally { this.transactionStatus = null; } } } /** * Start a new transaction. Only call this method if {@link #endTransaction()} * has been called. {@link #setComplete()} can be used again in the new transaction. * The fate of the new transaction, by default, will be the usual rollback. * @throws TransactionException if starting the transaction failed */ protected void startNewTransaction() throws TransactionException { if (this.transactionStatus != null) { throw new IllegalStateException("Cannot start new transaction without ending existing transaction: " + "Invoke endTransaction() before startNewTransaction()"); } if (this.transactionManager == null) { throw new IllegalStateException("No transaction manager set"); } this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition); ++this.transactionsStarted; this.complete = !this.defaultRollback; if (logger.isInfoEnabled()) { logger.info("Began transaction (" + this.transactionsStarted + "): transaction manager [" + this.transactionManager + "]; default rollback = " + this.defaultRollback); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy