001/*
002 * Copyright 2002-2019 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.testng;
018
019import javax.sql.DataSource;
020
021import org.springframework.beans.factory.annotation.Autowired;
022import org.springframework.context.ApplicationContext;
023import org.springframework.core.io.Resource;
024import org.springframework.dao.DataAccessException;
025import org.springframework.jdbc.core.JdbcTemplate;
026import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
027import org.springframework.lang.Nullable;
028import org.springframework.test.context.TestExecutionListeners;
029import org.springframework.test.context.event.EventPublishingTestExecutionListener;
030import org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener;
031import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
032import org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener;
033import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
034import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
035import org.springframework.test.context.web.ServletTestExecutionListener;
036import org.springframework.test.jdbc.JdbcTestUtils;
037import org.springframework.transaction.PlatformTransactionManager;
038import org.springframework.transaction.annotation.Transactional;
039import org.springframework.util.Assert;
040
041/**
042 * Abstract {@linkplain Transactional transactional} extension of
043 * {@link AbstractTestNGSpringContextTests} which adds convenience functionality
044 * for JDBC access. Expects a {@link DataSource} bean and a
045 * {@link PlatformTransactionManager} bean to be defined in the Spring
046 * {@linkplain ApplicationContext application context}.
047 *
048 * <p>This class exposes a {@link JdbcTemplate} and provides an easy way to
049 * {@linkplain #countRowsInTable count the number of rows in a table}
050 * (potentially {@linkplain #countRowsInTableWhere with a WHERE clause}),
051 * {@linkplain #deleteFromTables delete from tables},
052 * {@linkplain #dropTables drop tables}, and
053 * {@linkplain #executeSqlScript execute SQL scripts} within a transaction.
054 *
055 * <p>Concrete subclasses must fulfill the same requirements outlined in
056 * {@link AbstractTestNGSpringContextTests}.
057 *
058 * <p>The following {@link org.springframework.test.context.TestExecutionListener
059 * TestExecutionListeners} are configured by default:
060 *
061 * <ul>
062 * <li>{@link org.springframework.test.context.web.ServletTestExecutionListener}
063 * <li>{@link org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener}
064 * <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener}
065 * <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener}
066 * <li>{@link org.springframework.test.context.transaction.TransactionalTestExecutionListener}
067 * <li>{@link org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener}
068 * <li>{@link org.springframework.test.context.event.EventPublishingTestExecutionListener}
069 * </ul>
070 *
071 * @author Sam Brannen
072 * @author Juergen Hoeller
073 * @since 2.5
074 * @see AbstractTestNGSpringContextTests
075 * @see org.springframework.test.context.ContextConfiguration
076 * @see org.springframework.test.context.TestExecutionListeners
077 * @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
078 * @see org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
079 * @see org.springframework.transaction.annotation.Transactional
080 * @see org.springframework.test.annotation.Commit
081 * @see org.springframework.test.annotation.Rollback
082 * @see org.springframework.test.context.transaction.BeforeTransaction
083 * @see org.springframework.test.context.transaction.AfterTransaction
084 * @see org.springframework.test.jdbc.JdbcTestUtils
085 * @see org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
086 */
087@TestExecutionListeners(listeners = { ServletTestExecutionListener.class,
088        DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
089        DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
090        SqlScriptsTestExecutionListener.class, EventPublishingTestExecutionListener.class }, inheritListeners = false)
091@Transactional
092public abstract class AbstractTransactionalTestNGSpringContextTests extends AbstractTestNGSpringContextTests {
093
094        /**
095         * The {@code JdbcTemplate} that this base class manages, available to subclasses.
096         * @since 3.2
097         */
098        protected final JdbcTemplate jdbcTemplate = new JdbcTemplate();
099
100        @Nullable
101        private String sqlScriptEncoding;
102
103
104        /**
105         * Set the {@code DataSource}, typically provided via Dependency Injection.
106         * <p>This method also instantiates the {@link #jdbcTemplate} instance variable.
107         */
108        @Autowired
109        public void setDataSource(DataSource dataSource) {
110                this.jdbcTemplate.setDataSource(dataSource);
111        }
112
113        /**
114         * Specify the encoding for SQL scripts, if different from the platform encoding.
115         * @see #executeSqlScript
116         */
117        public void setSqlScriptEncoding(String sqlScriptEncoding) {
118                this.sqlScriptEncoding = sqlScriptEncoding;
119        }
120
121        /**
122         * Convenience method for counting the rows in the given table.
123         * @param tableName table name to count rows in
124         * @return the number of rows in the table
125         * @see JdbcTestUtils#countRowsInTable
126         */
127        protected int countRowsInTable(String tableName) {
128                return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
129        }
130
131        /**
132         * Convenience method for counting the rows in the given table, using the
133         * provided {@code WHERE} clause.
134         * <p>See the Javadoc for {@link JdbcTestUtils#countRowsInTableWhere} for details.
135         * @param tableName the name of the table to count rows in
136         * @param whereClause the {@code WHERE} clause to append to the query
137         * @return the number of rows in the table that match the provided
138         * {@code WHERE} clause
139         * @since 3.2
140         * @see JdbcTestUtils#countRowsInTableWhere
141         */
142        protected int countRowsInTableWhere(String tableName, String whereClause) {
143                return JdbcTestUtils.countRowsInTableWhere(this.jdbcTemplate, tableName, whereClause);
144        }
145
146        /**
147         * Convenience method for deleting all rows from the specified tables.
148         * <p>Use with caution outside of a transaction!
149         * @param names the names of the tables from which to delete
150         * @return the total number of rows deleted from all specified tables
151         * @see JdbcTestUtils#deleteFromTables
152         */
153        protected int deleteFromTables(String... names) {
154                return JdbcTestUtils.deleteFromTables(this.jdbcTemplate, names);
155        }
156
157        /**
158         * Convenience method for deleting all rows from the given table, using the
159         * provided {@code WHERE} clause.
160         * <p>Use with caution outside of a transaction!
161         * <p>See the Javadoc for {@link JdbcTestUtils#deleteFromTableWhere} for details.
162         * @param tableName the name of the table to delete rows from
163         * @param whereClause the {@code WHERE} clause to append to the query
164         * @param args arguments to bind to the query (leaving it to the {@code
165         * PreparedStatement} to guess the corresponding SQL type); may also contain
166         * {@link org.springframework.jdbc.core.SqlParameterValue SqlParameterValue}
167         * objects which indicate not only the argument value but also the SQL type
168         * and optionally the scale.
169         * @return the number of rows deleted from the table
170         * @since 4.0
171         * @see JdbcTestUtils#deleteFromTableWhere
172         */
173        protected int deleteFromTableWhere(String tableName, String whereClause, Object... args) {
174                return JdbcTestUtils.deleteFromTableWhere(this.jdbcTemplate, tableName, whereClause, args);
175        }
176
177        /**
178         * Convenience method for dropping all of the specified tables.
179         * <p>Use with caution outside of a transaction!
180         * @param names the names of the tables to drop
181         * @since 3.2
182         * @see JdbcTestUtils#dropTables
183         */
184        protected void dropTables(String... names) {
185                JdbcTestUtils.dropTables(this.jdbcTemplate, names);
186        }
187
188        /**
189         * Execute the given SQL script.
190         * <p>Use with caution outside of a transaction!
191         * <p>The script will normally be loaded by classpath.
192         * <p><b>Do not use this method to execute DDL if you expect rollback.</b>
193         * @param sqlResourcePath the Spring resource path for the SQL script
194         * @param continueOnError whether or not to continue without throwing an
195         * exception in the event of an error
196         * @throws DataAccessException if there is an error executing a statement
197         * @see ResourceDatabasePopulator
198         * @see #setSqlScriptEncoding
199         */
200        protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException {
201                DataSource ds = this.jdbcTemplate.getDataSource();
202                Assert.state(ds != null, "No DataSource set");
203                Assert.state(this.applicationContext != null, "No ApplicationContext available");
204                Resource resource = this.applicationContext.getResource(sqlResourcePath);
205                new ResourceDatabasePopulator(continueOnError, false, this.sqlScriptEncoding, resource).execute(ds);
206        }
207
208}