From e5d2570c8d9a005a7f9e0dd5b667528895ef4321 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 28 Apr 2009 07:29:24 +0000 Subject: [PATCH] [SPR-5145] Adding custom JUnit 4.5 Statements. --- .../AbstractJUnit4SpringContextTests.java | 26 ++--- ...TransactionalJUnit4SpringContextTests.java | 41 ++++--- .../junit4/SpringJUnit4ClassRunner.java | 67 ++++++----- .../RunSpringTestContextAfters.java | 104 ++++++++++++++++++ .../RunSpringTestContextBefores.java | 77 +++++++++++++ .../statements/SpringFailOnTimeout.java | 76 +++++++++++++ .../junit4/statements/SpringRepeat.java | 76 +++++++++++++ 7 files changed, 407 insertions(+), 60 deletions(-) create mode 100644 org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/RunSpringTestContextAfters.java create mode 100644 org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/RunSpringTestContextBefores.java create mode 100644 org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/SpringFailOnTimeout.java create mode 100644 org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/SpringRepeat.java diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java index 39fc1d7905..864e846bd5 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 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. @@ -19,7 +19,6 @@ package org.springframework.test.context.junit4; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.runner.RunWith; - import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.test.context.ContextConfiguration; @@ -31,18 +30,17 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe /** *

- * Abstract base test class which integrates the - * Spring TestContext Framework with explicit - * {@link ApplicationContext} testing support in a JUnit 4.4 - * environment. + * Abstract base test class which integrates the Spring TestContext + * Framework with explicit {@link ApplicationContext} testing support in a + * JUnit 4.5 environment. *

*

* Concrete subclasses should typically declare a class-level - * {@link ContextConfiguration @ContextConfiguration} annotation to configure - * the {@link ApplicationContext application context} + * {@link ContextConfiguration @ContextConfiguration} annotation to + * configure the {@link ApplicationContext application context} * {@link ContextConfiguration#locations() resource locations}. * If your test does not need to load an application context, you may choose - * to omit the {@link ContextConfiguration @ContextConfiguration} declaration + * to omit the {@link ContextConfiguration @ContextConfiguration} declaration * and to configure the appropriate * {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners} * manually. @@ -51,11 +49,11 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe * Note: this class serves only as a convenience for extension. If you do not * wish for your test classes to be tied to a Spring-specific class hierarchy, * you may configure your own custom test classes by using - * {@link SpringJUnit4ClassRunner}, - * {@link ContextConfiguration @ContextConfiguration}, - * {@link TestExecutionListeners @TestExecutionListeners}, etc. + * {@link SpringJUnit4ClassRunner}, {@link ContextConfiguration + * @ContextConfiguration}, {@link TestExecutionListeners + * @TestExecutionListeners}, etc. *

- * + * * @author Sam Brannen * @since 2.5 * @see ContextConfiguration @@ -66,7 +64,7 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe * @see org.springframework.test.context.testng.AbstractTestNGSpringContextTests */ @RunWith(SpringJUnit4ClassRunner.class) -@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class}) +@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class }) public abstract class AbstractJUnit4SpringContextTests implements ApplicationContextAware { /** diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java index 44703fe713..2f73fca311 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java @@ -54,12 +54,12 @@ import org.springframework.transaction.annotation.Transactional; * Note: this class serves only as a convenience for extension. If you do not * wish for your test classes to be tied to a Spring-specific class hierarchy, * you may configure your own custom test classes by using - * {@link SpringJUnit4ClassRunner}, - * {@link ContextConfiguration @ContextConfiguration}, - * {@link TestExecutionListeners @TestExecutionListeners}, - * {@link Transactional @Transactional}, etc. + * {@link SpringJUnit4ClassRunner}, {@link ContextConfiguration + * @ContextConfiguration}, {@link TestExecutionListeners + * @TestExecutionListeners}, {@link Transactional @Transactional}, + * etc. *

- * + * * @author Sam Brannen * @author Juergen Hoeller * @since 2.5 @@ -77,12 +77,13 @@ import org.springframework.transaction.annotation.Transactional; * @see org.springframework.test.context.junit38.AbstractTransactionalJUnit38SpringContextTests * @see org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests */ -@TestExecutionListeners({TransactionalTestExecutionListener.class}) +@TestExecutionListeners( { TransactionalTestExecutionListener.class }) @Transactional public abstract class AbstractTransactionalJUnit4SpringContextTests extends AbstractJUnit4SpringContextTests { /** - * The SimpleJdbcTemplate that this base class manages, available to subclasses. + * The SimpleJdbcTemplate that this base class manages, available to + * subclasses. */ protected SimpleJdbcTemplate simpleJdbcTemplate; @@ -98,16 +99,18 @@ public abstract class AbstractTransactionalJUnit4SpringContextTests extends Abst } /** - * Specify the encoding for SQL scripts, if different from the platform encoding. + * Specify the encoding for SQL scripts, if different from the platform + * encoding. + * * @see #executeSqlScript */ public void setSqlScriptEncoding(String sqlScriptEncoding) { this.sqlScriptEncoding = sqlScriptEncoding; } - /** * Count the rows in the given table. + * * @param tableName table name to count rows in * @return the number of rows in the table */ @@ -116,8 +119,9 @@ public abstract class AbstractTransactionalJUnit4SpringContextTests extends Abst } /** - * Convenience method for deleting all rows from the specified tables. - * Use with caution outside of a transaction! + * Convenience method for deleting all rows from the specified tables. Use + * with caution outside of a transaction! + * * @param names the names of the tables from which to delete * @return the total number of rows deleted from all specified tables */ @@ -127,21 +131,22 @@ public abstract class AbstractTransactionalJUnit4SpringContextTests extends Abst /** * Execute the given SQL script. Use with caution outside of a transaction! - *

The script will normally be loaded by classpath. There should be one statement - * per line. Any semicolons will be removed. Do not use this method to execute - * DDL if you expect rollback. + *

+ * The script will normally be loaded by classpath. There should be one + * statement per line. Any semicolons will be removed. Do not use this + * method to execute DDL if you expect rollback. + * * @param sqlResourcePath the Spring resource path for the SQL script * @param continueOnError whether or not to continue without throwing an * exception in the event of an error * @throws DataAccessException if there is an error executing a statement * and continueOnError was false */ - protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) - throws DataAccessException { + protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException { Resource resource = this.applicationContext.getResource(sqlResourcePath); - SimpleJdbcTestUtils.executeSqlScript( - this.simpleJdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding), continueOnError); + SimpleJdbcTestUtils.executeSqlScript(this.simpleJdbcTemplate, new EncodedResource(resource, + this.sqlScriptEncoding), continueOnError); } } diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java index 1b240b0c62..cb5569110e 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 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. @@ -26,39 +26,43 @@ import org.junit.internal.runners.JUnit4ClassRunner; import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; - import org.springframework.test.annotation.ProfileValueUtils; import org.springframework.test.context.TestContextManager; /** *

* SpringJUnit4ClassRunner is a custom extension of {@link JUnit4ClassRunner} - * which provides functionality of the Spring TestContext Framework - * to standard JUnit 4.4+ tests by means of the {@link TestContextManager} and + * which provides functionality of the Spring TestContext Framework to + * standard JUnit 4.5+ tests by means of the {@link TestContextManager} and * associated support classes and annotations. *

*

* The following list constitutes all annotations currently supported directly - * by SpringJUnit4ClassRunner. - * (Note that additional annotations may be supported by various - * {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners}) + * by SpringJUnit4ClassRunner. (Note that additional annotations + * may be supported by various + * {@link org.springframework.test.context.TestExecutionListener + * TestExecutionListeners}) *

* - * - *

NOTE: As of Spring 3.0 M1, SpringJUnit4ClassRunner requires - * JUnit 4.5, while internally still being based on JUnit 4.4 SPI. - * This will be rewritten based on JUnit 4.5's BlockJUnit4ClassRunner - * in a later Spring 3.0 release. - * + *

+ * NOTE: As of Spring 3.0 M1, SpringJUnit4ClassRunner requires JUnit 4.5, + * while internally still being based on JUnit 4.4 SPI. This will be rewritten + * based on JUnit 4.5's BlockJUnit4ClassRunner in a later Spring 3.0 release. + *

+ * * @author Sam Brannen * @author Juergen Hoeller * @since 2.5 @@ -75,6 +79,7 @@ public class SpringJUnit4ClassRunner extends JUnit4ClassRunner { * Constructs a new SpringJUnit4ClassRunner and initializes a * {@link TestContextManager} to provide Spring testing functionality to * standard JUnit tests. + * * @param clazz the Class object corresponding to the test class to be run * @see #createTestContextManager(Class) */ @@ -86,15 +91,16 @@ public class SpringJUnit4ClassRunner extends JUnit4ClassRunner { this.testContextManager = createTestContextManager(clazz); } - - @Override /** - * Check whether the test is enabled in the first place. This prevents classes with - * a non-matching @IfProfileValue annotation from running altogether, - * even skipping the execution of prepareTestInstance listener methods. + * Check whether the test is enabled in the first place. This prevents + * classes with a non-matching @IfProfileValue annotation + * from running altogether, even skipping the execution of + * prepareTestInstance listener methods. + * * @see org.springframework.test.annotation.IfProfileValue * @see org.springframework.test.context.TestExecutionListener */ + @Override public void run(RunNotifier notifier) { if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) { notifier.fireTestIgnored(getDescription()); @@ -108,6 +114,7 @@ public class SpringJUnit4ClassRunner extends JUnit4ClassRunner { * instance and then to a {@link TestContextManager} to * {@link TestContextManager#prepareTestInstance(Object) prepare} the test * instance for Spring testing functionality. + * * @see JUnit4ClassRunner#createTest() * @see TestContextManager#prepareTestInstance(Object) */ @@ -119,8 +126,11 @@ public class SpringJUnit4ClassRunner extends JUnit4ClassRunner { } /** - * Creates a new {@link TestContextManager}. Can be overridden by subclasses. - * @param clazz the Class object corresponding to the test class to be managed + * Creates a new {@link TestContextManager}. Can be overridden by + * subclasses. + * + * @param clazz the Class object corresponding to the test class to be + * managed */ protected TestContextManager createTestContextManager(Class clazz) { return new TestContextManager(clazz); @@ -136,6 +146,7 @@ public class SpringJUnit4ClassRunner extends JUnit4ClassRunner { /** * Invokes the supplied {@link Method test method} and notifies the supplied * {@link RunNotifier} of the appropriate events. + * * @see #createTest() * @see JUnit4ClassRunner#invokeTestMethod(Method,RunNotifier) */ diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/RunSpringTestContextAfters.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/RunSpringTestContextAfters.java new file mode 100644 index 0000000000..2912003dc8 --- /dev/null +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/RunSpringTestContextAfters.java @@ -0,0 +1,104 @@ +/* + * Copyright 2009 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.context.junit4.statements; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.junit.internal.runners.model.MultipleFailureException; +import org.junit.runners.model.Statement; +import org.springframework.test.context.TestContextManager; + +/** + * RunSpringTestContextAfters is a custom JUnit 4.5+ + * {@link Statement} which allows the Spring TestContext Framework to + * be plugged into the JUnit execution chain by calling + * {@link TestContextManager#afterTestMethod(Object, Method) afterTestMethod()} + * on the supplied {@link TestContextManager}. + * + * @see #evaluate() + * @see RunSpringTestContextBefores + * @author Sam Brannen + * @since 3.0 + */ +public class RunSpringTestContextAfters extends Statement { + + private final Statement next; + + private final Object testInstance; + + private final Method testMethod; + + private final TestContextManager testContextManager; + + + /** + * Constructs a new RunSpringTestContextAfters statement. + * + * @param next the next Statement in the execution chain + * @param testInstance the current test instance (never null) + * @param testMethod the test method which has just been executed on the + * test instance + * @param testContextManager the TestContextManager upon which to call + * afterTestMethod() + */ + public RunSpringTestContextAfters(Statement next, Object testInstance, Method testMethod, + TestContextManager testContextManager) { + this.next = next; + this.testInstance = testInstance; + this.testMethod = testMethod; + this.testContextManager = testContextManager; + } + + /** + * Invokes the next {@link Statement} in the execution chain (typically an + * instance of {@link org.junit.internal.runners.statements.RunAfters + * RunAfters}), catching any exceptions thrown, and then calls + * {@link TestContextManager#afterTestMethod(Object, Method)} with the first + * caught exception (if any). If the call to afterTestMethod() + * throws an exception, it will also be tracked. Multiple exceptions will be + * combined into a {@link MultipleFailureException}. + */ + @Override + public void evaluate() throws Throwable { + Throwable testException = null; + List errors = new ArrayList(); + try { + this.next.evaluate(); + } + catch (Throwable e) { + testException = e; + errors.add(e); + } + + try { + this.testContextManager.afterTestMethod(this.testInstance, this.testMethod, testException); + } + catch (Exception e) { + errors.add(e); + } + + if (errors.isEmpty()) { + return; + } + if (errors.size() == 1) { + throw errors.get(0); + } + throw new MultipleFailureException(errors); + } +} diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/RunSpringTestContextBefores.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/RunSpringTestContextBefores.java new file mode 100644 index 0000000000..49d91f1ac4 --- /dev/null +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/RunSpringTestContextBefores.java @@ -0,0 +1,77 @@ +/* + * Copyright 2009 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.context.junit4.statements; + +import java.lang.reflect.Method; + +import org.junit.runners.model.Statement; +import org.springframework.test.context.TestContextManager; + +/** + * RunSpringTestContextBefores is a custom JUnit 4.5+ + * {@link Statement} which allows the Spring TestContext Framework to + * be plugged into the JUnit execution chain by calling + * {@link TestContextManager#beforeTestMethod(Object, Method) + * beforeTestMethod()} on the supplied {@link TestContextManager}. + * + * @see #evaluate() + * @see RunSpringTestContextAfters + * @author Sam Brannen + * @since 3.0 + */ +public class RunSpringTestContextBefores extends Statement { + + private final Statement next; + + private final Object testInstance; + + private final Method testMethod; + + private final TestContextManager testContextManager; + + + /** + * Constructs a new RunSpringTestContextBefores statement. + * + * @param next the next Statement in the execution chain + * @param testInstance the current test instance (never null) + * @param testMethod the test method which is about to be executed on the + * test instance + * @param testContextManager the TestContextManager upon which to call + * beforeTestMethod() + */ + public RunSpringTestContextBefores(Statement next, Object testInstance, Method testMethod, + TestContextManager testContextManager) { + this.next = next; + this.testInstance = testInstance; + this.testMethod = testMethod; + this.testContextManager = testContextManager; + } + + /** + * Calls {@link TestContextManager#beforeTestMethod(Object, Method)} and + * then invokes the next {@link Statement} in the execution chain (typically + * an instance of {@link org.junit.internal.runners.statements.RunBefores + * RunBefores} ). + */ + @Override + public void evaluate() throws Throwable { + this.testContextManager.beforeTestMethod(this.testInstance, this.testMethod); + this.next.evaluate(); + } + +} diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/SpringFailOnTimeout.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/SpringFailOnTimeout.java new file mode 100644 index 0000000000..82b7e929ee --- /dev/null +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/SpringFailOnTimeout.java @@ -0,0 +1,76 @@ +/* + * Copyright 2009 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.context.junit4.statements; + +import java.util.concurrent.TimeoutException; + +import org.junit.runners.model.Statement; +import org.springframework.test.annotation.Timed; + +/** + * SpringFailOnTimeout is a custom JUnit 4.5+ {@link Statement} + * which adds support for Spring's {@link Timed @Timed} annotation by throwing + * an exception if the next statement in the execution chain takes more than the + * specified number of milliseconds. + * + * @see #evaluate() + * @author Sam Brannen + * @since 3.0 + */ +public class SpringFailOnTimeout extends Statement { + + private final Statement next; + + private final long timeout; + + + /** + * Constructs a new SpringFailOnTimeout statement. + * + * @param next the next Statement in the execution chain + * @param timeout the configured timeout for the current test + * @see Timed#millis() + */ + public SpringFailOnTimeout(Statement next, long timeout) { + this.next = next; + this.timeout = timeout; + } + + /** + * Invokes the next {@link Statement statement} in the execution chain + * (typically an instance of + * {@link org.junit.internal.runners.statements.InvokeMethod InvokeMethod} + * or {@link org.junit.internal.runners.statements.ExpectException + * ExpectException}) and throws an exception if the next + * statement takes more than the specified timeout + * . + */ + @Override + public void evaluate() throws Throwable { + long startTime = System.currentTimeMillis(); + try { + this.next.evaluate(); + } + finally { + long elapsed = System.currentTimeMillis() - startTime; + if (elapsed > this.timeout) { + throw new TimeoutException(String.format("Test took %s ms; limit was %s ms.", elapsed, this.timeout)); + } + } + } + +} diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/SpringRepeat.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/SpringRepeat.java new file mode 100644 index 0000000000..e18a3682b3 --- /dev/null +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/statements/SpringRepeat.java @@ -0,0 +1,76 @@ +/* + * Copyright 2009 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.context.junit4.statements; + +import java.lang.reflect.Method; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.runners.model.Statement; +import org.springframework.test.annotation.Repeat; +import org.springframework.util.ClassUtils; + +/** + * SpringRepeat is a custom JUnit 4.5+ {@link Statement} which adds + * support for Spring's {@link Repeat @Repeat} annotation by repeating the + * test for the specified number of times. + * + * @see #evaluate() + * @author Sam Brannen + * @since 3.0 + */ +public class SpringRepeat extends Statement { + + protected static final Log logger = LogFactory.getLog(SpringRepeat.class); + + private final Statement next; + + private final Method testMethod; + + private final int repeat; + + + /** + * Constructs a new SpringRepeat statement. + * + * @param next the next Statement in the execution chain + * @param testMethod the current test method + * @param repeat the configured repeat count for the current test method + * @see Repeat#value() + */ + public SpringRepeat(Statement next, Method testMethod, int repeat) { + this.next = next; + this.testMethod = testMethod; + this.repeat = Math.max(1, repeat); + } + + /** + * Invokes the next {@link Statement statement} in the execution chain for + * the specified repeat count. + */ + @Override + public void evaluate() throws Throwable { + for (int i = 0; i < this.repeat; i++) { + if (this.repeat > 1 && logger.isInfoEnabled()) { + logger.info(String.format("Repetition %d of test %s#%s()", (i + 1), + ClassUtils.getShortName(this.testMethod.getDeclaringClass()), this.testMethod.getName())); + } + this.next.evaluate(); + } + } + +}