org.springframework.test.context.TestContext#getTestClass ( )源码实例Demo

下面列出了org.springframework.test.context.TestContext#getTestClass ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。

/**
 * Forces context reload before test method if the annotation is present on the method or if the annotation is
 * present on the class and {@link ReloadContext.ClassMode} is set to <code>ClassMode.BEFORE_EACH_TEST_METHOD</code>
 * .
 */
@Override
public void beforeTestMethod(TestContext testContext) throws Exception {
	Class<?> testClass = testContext.getTestClass();
	Method testMethod = testContext.getTestMethod();
	final Class<ReloadContext> annotationType = ReloadContext.class;

	boolean methodReloadContext = testMethod.isAnnotationPresent(annotationType);
	boolean classReloadContext = testClass.isAnnotationPresent(annotationType);
	ReloadContext classReloadContextAnnotation = testClass.getAnnotation(annotationType);
	ClassMode classMode = classReloadContext ? classReloadContextAnnotation.classMode() : null;

	if (methodReloadContext || (classReloadContext && classMode == ClassMode.BEFORE_EACH_TEST_METHOD)) {
		reloadContext(testContext);
	}
}
 
/**
 * Perform the actual work for {@link #beforeTestClass} and {@link #afterTestClass}
 * by dirtying the context if appropriate (i.e., according to the required mode).
 * @param testContext the test context whose application context should
 * potentially be marked as dirty; never {@code null}
 * @param requiredClassMode the class mode required for a context to
 * be marked dirty in the current phase; never {@code null}
 * @throws Exception allows any exception to propagate
 * @since 4.2
 * @see #dirtyContext
 */
protected void beforeOrAfterTestClass(TestContext testContext, ClassMode requiredClassMode) throws Exception {
	Assert.notNull(testContext, "TestContext must not be null");
	Assert.notNull(requiredClassMode, "requiredClassMode must not be null");

	Class<?> testClass = testContext.getTestClass();
	Assert.notNull(testClass, "The test class of the supplied TestContext must not be null");

	DirtiesContext dirtiesContext = AnnotatedElementUtils.findMergedAnnotation(testClass, DirtiesContext.class);
	boolean classAnnotated = (dirtiesContext != null);
	ClassMode classMode = (classAnnotated ? dirtiesContext.classMode() : null);

	if (logger.isDebugEnabled()) {
		String phase = (requiredClassMode.name().startsWith("BEFORE") ? "Before" : "After");
		logger.debug(String.format(
			"%s test class: context %s, class annotated with @DirtiesContext [%s] with mode [%s].", phase,
			testContext, classAnnotated, classMode));
	}

	if (classMode == requiredClassMode) {
		dirtyContext(testContext, dirtiesContext.hierarchyMode());
	}
}
 
/**
 * Determine whether or not to rollback transactions by default for the
 * supplied {@linkplain TestContext test context}.
 * <p>Supports {@link Rollback @Rollback} or {@link Commit @Commit} at the
 * class-level.
 * @param testContext the test context for which the default rollback flag
 * should be retrieved
 * @return the <em>default rollback</em> flag for the supplied test context
 * @throws Exception if an error occurs while determining the default rollback flag
 */
protected final boolean isDefaultRollback(TestContext testContext) throws Exception {
	Class<?> testClass = testContext.getTestClass();
	Rollback rollback = AnnotatedElementUtils.findMergedAnnotation(testClass, Rollback.class);
	boolean rollbackPresent = (rollback != null);

	if (rollbackPresent) {
		boolean defaultRollback = rollback.value();
		if (logger.isDebugEnabled()) {
			logger.debug(String.format("Retrieved default @Rollback(%s) for test class [%s].",
					defaultRollback, testClass.getName()));
		}
		return defaultRollback;
	}

	// else
	return true;
}
 
/**
 * Helper method to build test execution information with test class and
 * method
 *
 * @param testContext of spring test environment
 *
 * @return String like &lt;Class Name&gt;[.&lt;Method Name&gt;]
 */
public static String getExecutionInformation(TestContext testContext) {
    String result = "";
    Class<?> testClass = testContext.getTestClass();

    result = testClass.getName();

    // now check for method
    Method m = null;

    try {
        m = testContext.getTestMethod();
    } catch (IllegalStateException ex) {
        // Do Nothing
    }

    if (m != null) {
        result = result + "." + m.getName();
    }

    return result;
}
 
/**
 * 初始化数据源
 *
 * @param testContext
 */
private void initDatabase(TestContext testContext) {
    SqlConfig.Database database = null;
    Class<?> testClass = testContext.getTestClass();
    SqlConfig sqlConfigInClass = testClass.getAnnotation(SqlConfig.class);
    if (sqlConfigInClass != null) {
        database = sqlConfigInClass.database();
    }
    Method method = testContext.getTestMethod();
    SqlConfig sqlConfigInMethod = method.getAnnotation(SqlConfig.class);
    if (sqlConfigInMethod != null) {
        database = sqlConfigInMethod.database();
    }
    if (database != null) {
        UnitTestDataSource.selectDataSource(database);
    }
}
 
/**
 * Retrieve the {@link TransactionConfigurationAttributes} for the
 * supplied {@link TestContext} whose {@linkplain Class test class}
 * may optionally declare or inherit
 * {@link TransactionConfiguration @TransactionConfiguration}.
 * <p>If {@code @TransactionConfiguration} is not present for the
 * supplied {@code TestContext}, a default instance of
 * {@code TransactionConfigurationAttributes} will be used instead.
 * @param testContext the test context for which the configuration
 * attributes should be retrieved
 * @return the TransactionConfigurationAttributes instance for this listener,
 * potentially cached
 * @see TransactionConfigurationAttributes#TransactionConfigurationAttributes()
 */
@SuppressWarnings("deprecation")
TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) {
	if (this.configurationAttributes == null) {
		Class<?> clazz = testContext.getTestClass();

		TransactionConfiguration txConfig = AnnotatedElementUtils.findMergedAnnotation(clazz,
			TransactionConfiguration.class);
		if (logger.isDebugEnabled()) {
			logger.debug(String.format("Retrieved @TransactionConfiguration [%s] for test class [%s].",
				txConfig, clazz.getName()));
		}

		TransactionConfigurationAttributes configAttributes = (txConfig == null ? defaultTxConfigAttributes
				: new TransactionConfigurationAttributes(txConfig.transactionManager(), txConfig.defaultRollback()));

		if (logger.isDebugEnabled()) {
			logger.debug(String.format("Using TransactionConfigurationAttributes %s for test class [%s].",
				configAttributes, clazz.getName()));
		}
		this.configurationAttributes = configAttributes;
	}
	return this.configurationAttributes;
}
 
/**
 * Invoke this method before test class will be created.</p>
 *
 * <b>Attention:</b> This will be only invoked if spring version &gt;= 3.x
 * are used.
 *
 * @param testContext
 *            default test context filled from spring
 *
 * @throws Exception
 *             if any error occurred
 */
public void beforeTestClass(final TestContext testContext) throws Exception {
    // no we check for the DBResetForClass
    final Class<?> testClass = testContext.getTestClass();

    handleFlywayTestAnnotationForClass(testContext, testClass);

    // now detect if current class has a beforeClass or BeforeAllAnnotation
    Class beforeClassClass = getClassOrNullForName("org.junit.BeforeClass");
    Class beforeAllClass = getClassOrNullForName("org.junit.jupiter.api.BeforeAll");
    Class beforeClassTestNgClass = getClassOrNullForName("org.testng.annotations.BeforeClass");

    // contains first finding of FlywayTest annotation together with a Before annotation
    handleFlywayTestWithTestAnnotation(testContext, testClass, beforeClassClass, beforeAllClass, beforeClassTestNgClass);
}
 
/**
 * Detect a default SQL script by implementing the algorithm defined in
 * {@link Sql#scripts}.
 */
private String detectDefaultScript(TestContext testContext, boolean classLevel) {
	Class<?> clazz = testContext.getTestClass();
	Method method = testContext.getTestMethod();
	String elementType = (classLevel ? "class" : "method");
	String elementName = (classLevel ? clazz.getName() : method.toString());

	String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName());
	if (!classLevel) {
		resourcePath += "." + method.getName();
	}
	resourcePath += ".sql";

	String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
	ClassPathResource classPathResource = new ClassPathResource(resourcePath);

	if (classPathResource.exists()) {
		if (logger.isInfoEnabled()) {
			logger.info(String.format("Detected default SQL script \"%s\" for test %s [%s]",
					prefixedResourcePath, elementType, elementName));
		}
		return prefixedResourcePath;
	}
	else {
		String msg = String.format("Could not detect default SQL script for test %s [%s]: " +
				"%s does not exist. Either declare statements or scripts via @Sql or make the " +
				"default SQL script available.", elementType, elementName, classPathResource);
		logger.error(msg);
		throw new IllegalStateException(msg);
	}
}
 
/**
 * Perform the actual work for {@link #beforeTestMethod} and {@link #afterTestMethod}
 * by dirtying the context if appropriate (i.e., according to the required modes).
 * @param testContext the test context whose application context should
 * potentially be marked as dirty; never {@code null}
 * @param requiredMethodMode the method mode required for a context to
 * be marked dirty in the current phase; never {@code null}
 * @param requiredClassMode the class mode required for a context to
 * be marked dirty in the current phase; never {@code null}
 * @throws Exception allows any exception to propagate
 * @since 4.2
 * @see #dirtyContext
 */
protected void beforeOrAfterTestMethod(TestContext testContext, MethodMode requiredMethodMode,
		ClassMode requiredClassMode) throws Exception {

	Assert.notNull(testContext, "TestContext must not be null");
	Assert.notNull(requiredMethodMode, "requiredMethodMode must not be null");
	Assert.notNull(requiredClassMode, "requiredClassMode must not be null");

	Class<?> testClass = testContext.getTestClass();
	Method testMethod = testContext.getTestMethod();
	Assert.notNull(testClass, "The test class of the supplied TestContext must not be null");
	Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null");

	DirtiesContext methodAnn = AnnotatedElementUtils.findMergedAnnotation(testMethod, DirtiesContext.class);
	DirtiesContext classAnn = AnnotatedElementUtils.findMergedAnnotation(testClass, DirtiesContext.class);
	boolean methodAnnotated = (methodAnn != null);
	boolean classAnnotated = (classAnn != null);
	MethodMode methodMode = (methodAnnotated ? methodAnn.methodMode() : null);
	ClassMode classMode = (classAnnotated ? classAnn.classMode() : null);

	if (logger.isDebugEnabled()) {
		String phase = (requiredClassMode.name().startsWith("BEFORE") ? "Before" : "After");
		logger.debug(String.format("%s test method: context %s, class annotated with @DirtiesContext [%s] "
				+ "with mode [%s], method annotated with @DirtiesContext [%s] with mode [%s].", phase, testContext,
			classAnnotated, classMode, methodAnnotated, methodMode));
	}

	if ((methodMode == requiredMethodMode) || (classMode == requiredClassMode)) {
		HierarchyMode hierarchyMode = (methodAnnotated ? methodAnn.hierarchyMode() : classAnn.hierarchyMode());
		dirtyContext(testContext, hierarchyMode);
	}
}
 
/**
 * implementation for annotation {@link FlywayTest} for handling with {@link org.testng.annotations.BeforeMethod}  annotation.
 *
 * @param testContext
 *            default test context filled from spring
 *
 * @throws Exception
 *             if any error occurred
 */
public void beforeTestExecution(final TestContext testContext)
        throws Exception {
    Class testClass = testContext.getTestClass();

    Class beforeMethodTestNgClass = getClassOrNullForName("org.testng.annotations.BeforeMethod");

    // contains first finding of FlywayTest annotation together with a Before annotation
    handleFlywayTestWithTestAnnotation(testContext, testClass, null, null, beforeMethodTestNgClass);
}
 
/**
 * Detect a default SQL script by implementing the algorithm defined in
 * {@link Sql#scripts}.
 */
private String detectDefaultScript(TestContext testContext, boolean classLevel) {
	Class<?> clazz = testContext.getTestClass();
	Method method = testContext.getTestMethod();
	String elementType = (classLevel ? "class" : "method");
	String elementName = (classLevel ? clazz.getName() : method.toString());

	String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName());
	if (!classLevel) {
		resourcePath += "." + method.getName();
	}
	resourcePath += ".sql";

	String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
	ClassPathResource classPathResource = new ClassPathResource(resourcePath);

	if (classPathResource.exists()) {
		if (logger.isInfoEnabled()) {
			logger.info(String.format("Detected default SQL script \"%s\" for test %s [%s]", prefixedResourcePath,
				elementType, elementName));
		}
		return prefixedResourcePath;
	}
	else {
		String msg = String.format("Could not detect default SQL script for test %s [%s]: "
				+ "%s does not exist. Either declare statements or scripts via @Sql or make the "
				+ "default SQL script available.", elementType, elementName, classPathResource);
		logger.error(msg);
		throw new IllegalStateException(msg);
	}
}
 
/**
 * Detect a default SQL script by implementing the algorithm defined in
 * {@link Sql#scripts}.
 */
private String detectDefaultScript(TestContext testContext, boolean classLevel) {
	Class<?> clazz = testContext.getTestClass();
	Method method = testContext.getTestMethod();
	String elementType = (classLevel ? "class" : "method");
	String elementName = (classLevel ? clazz.getName() : method.toString());

	String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName());
	if (!classLevel) {
		resourcePath += "." + method.getName();
	}
	resourcePath += ".sql";

	String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
	ClassPathResource classPathResource = new ClassPathResource(resourcePath);

	if (classPathResource.exists()) {
		if (logger.isInfoEnabled()) {
			logger.info(String.format("Detected default SQL script \"%s\" for test %s [%s]",
					prefixedResourcePath, elementType, elementName));
		}
		return prefixedResourcePath;
	}
	else {
		String msg = String.format("Could not detect default SQL script for test %s [%s]: " +
				"%s does not exist. Either declare statements or scripts via @Sql or make the " +
				"default SQL script available.", elementType, elementName, classPathResource);
		logger.error(msg);
		throw new IllegalStateException(msg);
	}
}
 
/**
 * Forces context reload before test method if the annotation is present on the class and
 * {@link ReloadContext.ClassMode} is set to <code>ClassMode.BEFORE_CLASS</code> .
 */
@Override
public void beforeTestClass(TestContext testContext) throws Exception {
	Class<?> testClass = testContext.getTestClass();
	final Class<ReloadContext> annotationType = ReloadContext.class;
	boolean reloadContext = testClass.isAnnotationPresent(ReloadContext.class);
	if (reloadContext) {
		ReloadContext reloadContextAnnotation = testClass.getAnnotation(annotationType);
		if (reloadContextAnnotation.classMode() == ClassMode.BEFORE_CLASS) {
			reloadContext(testContext);
		}
	}
}
 
@Override
public void afterTestClass(TestContext testContext) throws Exception {
    Class testClass = testContext.getTestClass();
    Logger logger = LoggerFactory.getLogger(testClass);
    logger.info("******** Shutting down test_class=" + testClass.getName());
    super.afterTestClass(testContext);
}
 
@Override
public void beforeTestClass(TestContext testContext) throws Exception {
    Class<?> testClass = testContext.getTestClass();

    FlywayTest[] annotations = findFlywayTestAnnotations(testClass);
    if (annotations.length > 1) {
        logger.warn("Optimized database loading is not supported when using multiple flyway test annotations");
    }
    for (FlywayTest annotation : annotations) {
        optimizedDbReset(testContext, testClass, annotation);
    }
}
 
private void initTestData(TestContext testContext) {
    List<String> sqlFiles = new ArrayList<String>();
    /**
     * 读取测试类指定的sql文件
     */
    Class<?> testClass = testContext.getTestClass();
    SqlConfig sqlConfigInClass = testClass.getAnnotation(SqlConfig.class);
    if (sqlConfigInClass != null) {
        String[] sqlFilesInClass = sqlConfigInClass.sqlFiles();
        if (ArrayUtils.isNotEmpty(sqlFilesInClass)) {
            sqlFiles.addAll(Arrays.asList(sqlFilesInClass));
        }
    }
    /**
     * 读取测试方法指定的sql文件
     */
    Method method = testContext.getTestMethod();
    SqlConfig sqlConfigInMethod = method.getAnnotation(SqlConfig.class);
    if (sqlConfigInMethod != null) {
        String[] sqlFilesInMethod = sqlConfigInMethod.sqlFiles();
        if (ArrayUtils.isNotEmpty(sqlFilesInMethod)) {
            sqlFiles.addAll(Arrays.asList(sqlFilesInMethod));
        }
    }
    /**
     * 执行sql
     */
    for (String sqlFile : sqlFiles) {
        LOGGER.info(String.format("execute sql file [%s]", sqlFile));
        this.executeSqlScript(testContext, sqlFile, false);
    }
}
 
/**
 * If the test method of the supplied {@linkplain TestContext test context}
 * is configured to run within a transaction, this method will run
 * {@link BeforeTransaction @BeforeTransaction} methods and start a new
 * transaction.
 * <p>Note that if a {@code @BeforeTransaction} method fails, any remaining
 * {@code @BeforeTransaction} methods will not be invoked, and a transaction
 * will not be started.
 * @see org.springframework.transaction.annotation.Transactional
 * @see #getTransactionManager(TestContext, String)
 */
@Override
public void beforeTestMethod(final TestContext testContext) throws Exception {
	final Method testMethod = testContext.getTestMethod();
	final Class<?> testClass = testContext.getTestClass();
	Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null");

	TransactionContext txContext = TransactionContextHolder.removeCurrentTransactionContext();
	if (txContext != null) {
		throw new IllegalStateException("Cannot start a new transaction without ending the existing transaction.");
	}

	PlatformTransactionManager tm = null;
	TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, testClass);

	if (transactionAttribute != null) {
		transactionAttribute = TestContextTransactionUtils.createDelegatingTransactionAttribute(testContext,
			transactionAttribute);

		if (logger.isDebugEnabled()) {
			logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context "
					+ testContext);
		}

		if (transactionAttribute.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			return;
		}

		tm = getTransactionManager(testContext, transactionAttribute.getQualifier());
	}

	if (tm != null) {
		txContext = new TransactionContext(testContext, tm, transactionAttribute, isRollback(testContext));
		runBeforeTransactionMethods(testContext);
		txContext.startTransaction();
		TransactionContextHolder.setCurrentTransactionContext(txContext);
	}
}
 
/**
 * implementation for annotation {@link FlywayTest} for handling with {@link org.junit.Before} or
 * {@link org.junit.jupiter.api.BeforeEach}  annotation.
 *
 * @param testContext
 *            default test context filled from spring
 *
 * @throws Exception
 *             if any error occurred
 */
public void prepareTestInstance(final TestContext testContext)
        throws Exception {
    Class testClass = testContext.getTestClass();

    Class beforeMethodClass = getClassOrNullForName("org.junit.Before");
    Class beforeEachMethodClass = getClassOrNullForName("org.junit.jupiter.api.BeforeEach");

    // contains first finding of FlywayTest annotation together with a Before annotation
    handleFlywayTestWithTestAnnotation(testContext, testClass, beforeMethodClass, beforeEachMethodClass, null);
}
 
/**
 * Execute the SQL scripts configured via the supplied {@link Sql @Sql}
 * annotation for the given {@link ExecutionPhase} and {@link TestContext}.
 * <p>Special care must be taken in order to properly support the configured
 * {@link SqlConfig#transactionMode}.
 * @param sql the {@code @Sql} annotation to parse
 * @param executionPhase the current execution phase
 * @param testContext the current {@code TestContext}
 * @param classLevel {@code true} if {@link Sql @Sql} was declared at the class level
 */
private void executeSqlScripts(Sql sql, ExecutionPhase executionPhase, TestContext testContext, boolean classLevel)
		throws Exception {

	if (executionPhase != sql.executionPhase()) {
		return;
	}

	MergedSqlConfig mergedSqlConfig = new MergedSqlConfig(sql.config(), testContext.getTestClass());
	if (logger.isDebugEnabled()) {
		logger.debug(String.format("Processing %s for execution phase [%s] and test context %s.",
				mergedSqlConfig, executionPhase, testContext));
	}

	final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
	populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding());
	populator.setSeparator(mergedSqlConfig.getSeparator());
	populator.setCommentPrefix(mergedSqlConfig.getCommentPrefix());
	populator.setBlockCommentStartDelimiter(mergedSqlConfig.getBlockCommentStartDelimiter());
	populator.setBlockCommentEndDelimiter(mergedSqlConfig.getBlockCommentEndDelimiter());
	populator.setContinueOnError(mergedSqlConfig.getErrorMode() == ErrorMode.CONTINUE_ON_ERROR);
	populator.setIgnoreFailedDrops(mergedSqlConfig.getErrorMode() == ErrorMode.IGNORE_FAILED_DROPS);

	String[] scripts = getScripts(sql, testContext, classLevel);
	scripts = TestContextResourceUtils.convertToClasspathResourcePaths(testContext.getTestClass(), scripts);
	List<Resource> scriptResources = TestContextResourceUtils.convertToResourceList(
			testContext.getApplicationContext(), scripts);
	for (String stmt : sql.statements()) {
		if (StringUtils.hasText(stmt)) {
			stmt = stmt.trim();
			scriptResources.add(new ByteArrayResource(stmt.getBytes(), "from inlined SQL statement: " + stmt));
		}
	}
	populator.setScripts(scriptResources.toArray(new Resource[0]));
	if (logger.isDebugEnabled()) {
		logger.debug("Executing SQL scripts: " + ObjectUtils.nullSafeToString(scriptResources));
	}

	String dsName = mergedSqlConfig.getDataSource();
	String tmName = mergedSqlConfig.getTransactionManager();
	DataSource dataSource = TestContextTransactionUtils.retrieveDataSource(testContext, dsName);
	PlatformTransactionManager txMgr = TestContextTransactionUtils.retrieveTransactionManager(testContext, tmName);
	boolean newTxRequired = (mergedSqlConfig.getTransactionMode() == TransactionMode.ISOLATED);

	if (txMgr == null) {
		Assert.state(!newTxRequired, () -> String.format("Failed to execute SQL scripts for test context %s: " +
				"cannot execute SQL scripts using Transaction Mode " +
				"[%s] without a PlatformTransactionManager.", testContext, TransactionMode.ISOLATED));
		Assert.state(dataSource != null, () -> String.format("Failed to execute SQL scripts for test context %s: " +
				"supply at least a DataSource or PlatformTransactionManager.", testContext));
		// Execute scripts directly against the DataSource
		populator.execute(dataSource);
	}
	else {
		DataSource dataSourceFromTxMgr = getDataSourceFromTransactionManager(txMgr);
		// Ensure user configured an appropriate DataSource/TransactionManager pair.
		if (dataSource != null && dataSourceFromTxMgr != null && !dataSource.equals(dataSourceFromTxMgr)) {
			throw new IllegalStateException(String.format("Failed to execute SQL scripts for test context %s: " +
					"the configured DataSource [%s] (named '%s') is not the one associated with " +
					"transaction manager [%s] (named '%s').", testContext, dataSource.getClass().getName(),
					dsName, txMgr.getClass().getName(), tmName));
		}
		if (dataSource == null) {
			dataSource = dataSourceFromTxMgr;
			Assert.state(dataSource != null, () -> String.format("Failed to execute SQL scripts for " +
					"test context %s: could not obtain DataSource from transaction manager [%s] (named '%s').",
					testContext, txMgr.getClass().getName(), tmName));
		}
		final DataSource finalDataSource = dataSource;
		int propagation = (newTxRequired ? TransactionDefinition.PROPAGATION_REQUIRES_NEW :
				TransactionDefinition.PROPAGATION_REQUIRED);
		TransactionAttribute txAttr = TestContextTransactionUtils.createDelegatingTransactionAttribute(
				testContext, new DefaultTransactionAttribute(propagation));
		new TransactionTemplate(txMgr, txAttr).execute(status -> {
			populator.execute(finalDataSource);
			return null;
		});
	}
}
 
/**
 * Execute the SQL scripts configured via the supplied {@link Sql @Sql}
 * annotation for the given {@link ExecutionPhase} and {@link TestContext}.
 * <p>Special care must be taken in order to properly support the configured
 * {@link SqlConfig#transactionMode}.
 * @param sql the {@code @Sql} annotation to parse
 * @param executionPhase the current execution phase
 * @param testContext the current {@code TestContext}
 * @param classLevel {@code true} if {@link Sql @Sql} was declared at the class level
 */
private void executeSqlScripts(Sql sql, ExecutionPhase executionPhase, TestContext testContext, boolean classLevel)
		throws Exception {

	if (executionPhase != sql.executionPhase()) {
		return;
	}

	MergedSqlConfig mergedSqlConfig = new MergedSqlConfig(sql.config(), testContext.getTestClass());
	if (logger.isDebugEnabled()) {
		logger.debug(String.format("Processing %s for execution phase [%s] and test context %s.",
				mergedSqlConfig, executionPhase, testContext));
	}

	final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
	populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding());
	populator.setSeparator(mergedSqlConfig.getSeparator());
	populator.setCommentPrefix(mergedSqlConfig.getCommentPrefix());
	populator.setBlockCommentStartDelimiter(mergedSqlConfig.getBlockCommentStartDelimiter());
	populator.setBlockCommentEndDelimiter(mergedSqlConfig.getBlockCommentEndDelimiter());
	populator.setContinueOnError(mergedSqlConfig.getErrorMode() == ErrorMode.CONTINUE_ON_ERROR);
	populator.setIgnoreFailedDrops(mergedSqlConfig.getErrorMode() == ErrorMode.IGNORE_FAILED_DROPS);

	String[] scripts = getScripts(sql, testContext, classLevel);
	scripts = TestContextResourceUtils.convertToClasspathResourcePaths(testContext.getTestClass(), scripts);
	List<Resource> scriptResources = TestContextResourceUtils.convertToResourceList(
			testContext.getApplicationContext(), scripts);
	for (String stmt : sql.statements()) {
		if (StringUtils.hasText(stmt)) {
			stmt = stmt.trim();
			scriptResources.add(new ByteArrayResource(stmt.getBytes(), "from inlined SQL statement: " + stmt));
		}
	}
	populator.setScripts(scriptResources.toArray(new Resource[0]));
	if (logger.isDebugEnabled()) {
		logger.debug("Executing SQL scripts: " + ObjectUtils.nullSafeToString(scriptResources));
	}

	String dsName = mergedSqlConfig.getDataSource();
	String tmName = mergedSqlConfig.getTransactionManager();
	DataSource dataSource = TestContextTransactionUtils.retrieveDataSource(testContext, dsName);
	PlatformTransactionManager txMgr = TestContextTransactionUtils.retrieveTransactionManager(testContext, tmName);
	boolean newTxRequired = (mergedSqlConfig.getTransactionMode() == TransactionMode.ISOLATED);

	if (txMgr == null) {
		Assert.state(!newTxRequired, () -> String.format("Failed to execute SQL scripts for test context %s: " +
				"cannot execute SQL scripts using Transaction Mode " +
				"[%s] without a PlatformTransactionManager.", testContext, TransactionMode.ISOLATED));
		Assert.state(dataSource != null, () -> String.format("Failed to execute SQL scripts for test context %s: " +
				"supply at least a DataSource or PlatformTransactionManager.", testContext));
		// Execute scripts directly against the DataSource
		populator.execute(dataSource);
	}
	else {
		DataSource dataSourceFromTxMgr = getDataSourceFromTransactionManager(txMgr);
		// Ensure user configured an appropriate DataSource/TransactionManager pair.
		if (dataSource != null && dataSourceFromTxMgr != null && !dataSource.equals(dataSourceFromTxMgr)) {
			throw new IllegalStateException(String.format("Failed to execute SQL scripts for test context %s: " +
					"the configured DataSource [%s] (named '%s') is not the one associated with " +
					"transaction manager [%s] (named '%s').", testContext, dataSource.getClass().getName(),
					dsName, txMgr.getClass().getName(), tmName));
		}
		if (dataSource == null) {
			dataSource = dataSourceFromTxMgr;
			Assert.state(dataSource != null, () -> String.format("Failed to execute SQL scripts for " +
					"test context %s: could not obtain DataSource from transaction manager [%s] (named '%s').",
					testContext, txMgr.getClass().getName(), tmName));
		}
		final DataSource finalDataSource = dataSource;
		int propagation = (newTxRequired ? TransactionDefinition.PROPAGATION_REQUIRES_NEW :
				TransactionDefinition.PROPAGATION_REQUIRED);
		TransactionAttribute txAttr = TestContextTransactionUtils.createDelegatingTransactionAttribute(
				testContext, new DefaultTransactionAttribute(propagation));
		new TransactionTemplate(txMgr, txAttr).execute(status -> {
			populator.execute(finalDataSource);
			return null;
		});
	}
}