diff --git a/spring-test/src/test/java/org/springframework/test/context/TestContextConcurrencyTests.java b/spring-test/src/test/java/org/springframework/test/context/TestContextConcurrencyTests.java index 6a847232a4..782953e4fe 100644 --- a/spring-test/src/test/java/org/springframework/test/context/TestContextConcurrencyTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/TestContextConcurrencyTests.java @@ -41,6 +41,7 @@ import static org.junit.Assert.assertThat; * * @author Sam Brannen * @since 5.0 + * @see org.springframework.test.context.junit4.concurrency.SpringJUnit4ConcurrencyTests */ public class TestContextConcurrencyTests { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/JUnitTestingUtils.java b/spring-test/src/test/java/org/springframework/test/context/junit4/JUnitTestingUtils.java index 696d5f4b61..f0c3743450 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/JUnitTestingUtils.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/JUnitTestingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -18,6 +18,9 @@ package org.springframework.test.context.junit4; import java.lang.reflect.Constructor; +import org.junit.experimental.ParallelComputer; +import org.junit.jupiter.api.Assertions; +import org.junit.runner.Computer; import org.junit.runner.JUnitCore; import org.junit.runner.RunWith; import org.junit.runner.Runner; @@ -25,10 +28,15 @@ import org.junit.runner.notification.RunNotifier; import org.springframework.beans.BeanUtils; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; /** - * Collection of utilities for testing the execution of JUnit tests. + * Collection of utilities for testing the execution of JUnit 4 based tests. + * + *

Note that these utilities use {@link Assertions} from JUnit Jupiter, + * but that should not result in any adverse side effects in terms of + * proper test failure for failed assertions. * * @author Sam Brannen * @since 4.2 @@ -38,8 +46,8 @@ public class JUnitTestingUtils { /** * Run the tests in the supplied {@code testClass}, using the {@link Runner} - * it is configured with (i.e., via {@link RunWith @RunWith}) or the default - * JUnit runner, and assert the expectations of the test execution. + * configured via {@link RunWith @RunWith} or the default JUnit runner, and + * assert the expectations of the test execution. * * @param testClass the test class to run with JUnit * @param expectedStartedCount the expected number of tests that started @@ -65,7 +73,7 @@ public class JUnitTestingUtils { * (i.e., via {@link RunWith @RunWith}) or the default JUnit runner. * * @param runnerClass the explicit runner class to use or {@code null} - * if the implicit runner should be used + * if the default JUnit runner should be used * @param testClass the test class to run with JUnit * @param expectedStartedCount the expected number of tests that started * @param expectedFailedCount the expected number of tests that failed @@ -93,11 +101,54 @@ public class JUnitTestingUtils { junit.run(testClass); } - assertEquals("tests started for [" + testClass + "]:", expectedStartedCount, listener.getTestStartedCount()); - assertEquals("tests failed for [" + testClass + "]:", expectedFailedCount, listener.getTestFailureCount()); - assertEquals("tests finished for [" + testClass + "]:", expectedFinishedCount, listener.getTestFinishedCount()); - assertEquals("tests ignored for [" + testClass + "]:", expectedIgnoredCount, listener.getTestIgnoredCount()); - assertEquals("failed assumptions for [" + testClass + "]:", expectedAssumptionFailedCount, listener.getTestAssumptionFailureCount()); + // @formatter:off + assertAll( + () -> assertEquals(expectedStartedCount, listener.getTestStartedCount(), "tests started for [" + testClass + "]"), + () -> assertEquals(expectedFailedCount, listener.getTestFailureCount(), "tests failed for [" + testClass + "]"), + () -> assertEquals(expectedFinishedCount, listener.getTestFinishedCount(), "tests finished for [" + testClass + "]"), + () -> assertEquals(expectedIgnoredCount, listener.getTestIgnoredCount(), "tests ignored for [" + testClass + "]"), + () -> assertEquals(expectedAssumptionFailedCount, listener.getTestAssumptionFailureCount(), "failed assumptions for [" + testClass + "]") + ); + // @formatter:on + } + + /** + * Run all tests in the supplied test classes according to the policies of + * the supplied {@link Computer}, using the {@link Runner} configured via + * {@link RunWith @RunWith} or the default JUnit runner, and assert the + * expectations of the test execution. + * + *

To have all tests executed in parallel, supply {@link ParallelComputer#methods()} + * as the {@code Computer}. To have all tests executed serially, supply + * {@link Computer#serial()} as the {@code Computer}. + * + * @param computer the JUnit {@code Computer} to use + * @param expectedStartedCount the expected number of tests that started + * @param expectedFailedCount the expected number of tests that failed + * @param expectedFinishedCount the expected number of tests that finished + * @param expectedIgnoredCount the expected number of tests that were ignored + * @param expectedAssumptionFailedCount the expected number of tests that + * resulted in a failed assumption + * @param testClasses one or more test classes to run + */ + public static void runTestsAndAssertCounters(Computer computer, int expectedStartedCount, int expectedFailedCount, + int expectedFinishedCount, int expectedIgnoredCount, int expectedAssumptionFailedCount, + Class... testClasses) throws Exception { + + JUnitCore junit = new JUnitCore(); + TrackingRunListener listener = new TrackingRunListener(); + junit.addListener(listener); + junit.run(computer, testClasses); + + // @formatter:off + assertAll( + () -> assertEquals(expectedStartedCount, listener.getTestStartedCount(), "tests started"), + () -> assertEquals(expectedFailedCount, listener.getTestFailureCount(), "tests failed"), + () -> assertEquals(expectedFinishedCount, listener.getTestFinishedCount(), "tests finished"), + () -> assertEquals(expectedIgnoredCount, listener.getTestIgnoredCount(), "tests ignored"), + () -> assertEquals(expectedAssumptionFailedCount, listener.getTestAssumptionFailureCount(), "failed assumptions") + ); + // @formatter:on } } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/concurrency/SpringJUnit4ConcurrencyTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/concurrency/SpringJUnit4ConcurrencyTests.java new file mode 100644 index 0000000000..88b1b640cd --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/concurrency/SpringJUnit4ConcurrencyTests.java @@ -0,0 +1,119 @@ +/* + * Copyright 2002-2016 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.concurrency; + +import org.junit.Test; +import org.junit.experimental.ParallelComputer; + +import org.springframework.test.context.hierarchies.web.DispatcherWacRootWacEarTests; +import org.springframework.test.context.junit4.InheritedConfigSpringJUnit4ClassRunnerAppCtxTests; +import org.springframework.test.context.junit4.MethodLevelTransactionalSpringRunnerTests; +import org.springframework.test.context.junit4.SpringJUnit47ClassRunnerRuleTests; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunnerAppCtxTests; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit4.TimedTransactionalSpringRunnerTests; +import org.springframework.test.context.junit4.rules.BasicAnnotationConfigWacSpringRuleTests; +import org.springframework.test.context.junit4.rules.ParameterizedSpringRuleTests; +import org.springframework.test.context.junit4.rules.SpringClassRule; +import org.springframework.test.context.junit4.rules.SpringMethodRule; +import org.springframework.test.context.web.RequestAndSessionScopedBeansWacTests; +import org.springframework.test.context.web.socket.WebSocketServletServerContainerFactoryBeanTests; +import org.springframework.test.web.client.samples.SampleTests; +import org.springframework.test.web.servlet.samples.context.JavaConfigTests; +import org.springframework.test.web.servlet.samples.context.WebAppResourceTests; +import org.springframework.tests.Assume; +import org.springframework.tests.TestGroup; + +import static org.springframework.test.context.junit4.JUnitTestingUtils.runTestsAndAssertCounters; + +/** + * Concurrency tests for the {@link SpringRunner}, {@link SpringClassRule}, and + * {@link SpringMethodRule} that use JUnit 4's experimental {@link ParallelComputer} + * to execute tests in parallel. + * + *

The tests executed by this test class come from a hand-picked collection of test + * classes within the test suite that is intended to cover most categories of tests + * that are currently supported by the TestContext Framework on JUnit 4. + * + *

The chosen test classes intentionally do not include any classes that + * fall under the following categories. + * + *

+ * + *

NOTE: these tests only run if the {@link TestGroup#LONG_RUNNING + * LONG_RUNNING} test group is enabled. + * + * @author Sam Brannen + * @since 5.0 + * @see org.springframework.test.context.TestContextConcurrencyTests + */ +public class SpringJUnit4ConcurrencyTests { + + // @formatter:off + private static final Class[] testClasses = new Class[] { + // Basics + /* 9 */ SpringJUnit4ClassRunnerAppCtxTests.class, + /* 9 */ InheritedConfigSpringJUnit4ClassRunnerAppCtxTests.class, + /* 2 */ SpringJUnit47ClassRunnerRuleTests.class, + /* 2 */ ParameterizedSpringRuleTests.class, + // Transactional + /* 2 */ MethodLevelTransactionalSpringRunnerTests.class, + /* 4 */ TimedTransactionalSpringRunnerTests.class, + // Web and Scopes + /* 1 */ DispatcherWacRootWacEarTests.class, /* 2 ignored */ + /* 3 */ BasicAnnotationConfigWacSpringRuleTests.class, + /* 2 */ RequestAndSessionScopedBeansWacTests.class, + /* 1 */ WebSocketServletServerContainerFactoryBeanTests.class, + // Spring MVC Test + /* 2 */ JavaConfigTests.class, + /* 3 */ WebAppResourceTests.class, + /* 4 */ SampleTests.class + }; + // @formatter:on + + /** + * The number of tests in all {@link #testClasses}. + * + *

The current number of tests per test class is tracked as a comment + * before each class reference above. The following constant must therefore + * be the sum of those values. + * + *

This is admittedly fragile, but there's unfortunately not really a + * better way to count the number of tests without re-implementing JUnit 4's + * discovery algorithm. Plus, the presence of parameterized tests makes it + * even more difficult to count programmatically. + */ + private static final int TESTS = 44; + private static final int FAILED = 0; + private static final int IGNORED = 2; + private static final int ABORTED = 0; + + + @Test + public void runAllTestsConcurrently() throws Exception { + + Assume.group(TestGroup.LONG_RUNNING); + + runTestsAndAssertCounters(new ParallelComputer(true, true), TESTS, FAILED, TESTS, IGNORED, ABORTED, + testClasses); + } + +}