diff --git a/README.md b/README.md index 062be28..fd62da6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -# `spring-test-junit5` +# Spring JUnit 5 Testing Support -This project serves as a proof of concept for how the _Spring TestContext Framework_ -can be fully integrated into the current [JUnit 5] snapshots using a single `Extension`. +This project serves as the official prototype for [JUnit 5][] testing support +in the [Spring TestContext Framework][] which will eventually be incorporated +into [Spring Framework][] 5.0 in conjunction with [SPR-13575][]. # Using the `SpringExtension` Currently, all that's needed to use the _Spring TestContext Framework_ with JUnit 5 -is to annotate your JUnit 5 based test class with `@ExtendWith(SpringExtension.class)` +is to annotate a JUnit 5 based test class with `@ExtendWith(SpringExtension.class)` and whatever Spring annotations you need (e.g., `@ContextConfiguration`, `@Transactional`, `@Sql`, etc.). See [`SpringExtensionTests`] for an example of this extension in action, and check out the source code of [`SpringExtension`] if you're interested in the @@ -20,17 +21,59 @@ custom annotations that are composed from Spring annotations **and** JUnit 5 annotations. Take a look at [`@SpringJUnit5Config`] for an example, and check out [`ComposedSpringExtensionTests`] for an example of `@SpringJUnit5Config` in action. +# License + +This project is released under version 2.0 of the [Apache License][]. + +# Artifacts + +There are currently no downloadable artifacts for this project. +However, if you install in a local Maven repository (see below) +the generated artifact will correspond to the following. + + - **Group ID**: `org.springframework.test` + - **Artifact ID**: `spring-test-junit5` + - **Version**: `1.0.0.BUILD-SNAPSHOT` + +# Building from Source + +This project uses a [Gradle][]-based build system. In the instructions +below, `./gradlew` is invoked from the root of the project and serves as +a cross-platform, self-contained bootstrap mechanism for the build. + +## Prerequisites and Dependencies + +- [Git][] +- [JDK 8][JDK8] update 60 or later +- [JUnit 5][] `5.0.0-SNAPSHOT` +- [Spring Framework][] `4.3.0.BUILD-SNAPSHOT` + +Be sure that your `JAVA_HOME` environment variable points to the `jdk1.8.0` folder +extracted from the JDK download. + +## Compile and Test + +Build all JARs, distribution ZIP files, and docs: + +`./gradlew build` + +## Install `spring-test-junit5` in local Maven repository + +`./gradlew install` + # Running Tests with Gradle Executing `gradlew clean test` from the command line should result in output similar to the following. ``` -Test run finished after 512 ms -[ 4 tests found ] +:junit5Test + +Test run finished after 1295 ms +[ 24 tests found ] [ 0 tests skipped ] -[ 4 tests started ] +[ 24 tests started ] [ 0 tests aborted ] -[ 4 tests successful] +[ 24 tests successful] [ 0 tests failed ] ``` @@ -38,11 +81,19 @@ Test run finished after 512 ms In order to execute the tests within an IDE, simply run [`SpringExtensionTestSuite`] as a JUnit 4 test class. +---- -[JUnit 5]: https://github.com/junit-team/junit-lambda +[Apache License]: http://www.apache.org/licenses/LICENSE-2.0 [composed annotations]: https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model#composed-annotations +[Git]: http://help.github.com/set-up-git-redirect +[Gradle]: http://gradle.org +[JDK8]: http://www.oracle.com/technetwork/java/javase/downloads +[JUnit 5]: https://github.com/junit-team/junit5 +[SPR-13575]: https://jira.spring.io/browse/SPR-13575 +[Spring Framework]: http://projects.spring.io/spring-framework/ +[Spring TestContext Framework]: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#testcontext-framework +[`@SpringJUnit5Config`]: https://github.com/sbrannen/spring-test-junit5/blob/master/src/test/java/org/springframework/test/context/junit5/SpringJUnit5Config.java [`ComposedSpringExtensionTests`]: https://github.com/sbrannen/spring-test-junit5/blob/master/src/test/java/org/springframework/test/context/junit5/ComposedSpringExtensionTests.java -[`SpringExtension`]: https://github.com/sbrannen/spring-test-junit5/blob/master/src/main/java/org/springframework/test/context/junit5/SpringExtension.java -[`SpringExtensionTests`]: https://github.com/sbrannen/spring-test-junit5/blob/master/src/test/java/org/springframework/test/context/junit5/SpringExtensionTests.java [`SpringExtensionTestSuite`]: https://github.com/sbrannen/spring-test-junit5/blob/master/src/test/java/org/springframework/test/context/junit5/SpringExtensionTestSuite.java -[`@SpringJUnit5Config`]: https://github.com/sbrannen/spring-test-junit5/blob/master/src/test/java/org/springframework/test/context/junit5/SpringJUnit5Config.java +[`SpringExtensionTests`]: https://github.com/sbrannen/spring-test-junit5/blob/master/src/test/java/org/springframework/test/context/junit5/SpringExtensionTests.java +[`SpringExtension`]: https://github.com/sbrannen/spring-test-junit5/blob/master/src/main/java/org/springframework/test/context/junit5/SpringExtension.java diff --git a/build.gradle b/build.gradle index 7e865dc..994aec9 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ buildscript { apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' +apply plugin: 'maven' apply plugin: 'org.junit.gen5.gradle' jar { @@ -17,23 +18,20 @@ jar { version = '1.0.0-SNAPSHOT' } +group = 'org.springframework.test' + ext.hamcrestVersion = '1.3' ext.jacksonVersion = '2.7.0' ext.jsonpathVersion = '2.1.0' ext.junit5Version = '5.0.0-SNAPSHOT' ext.log4JVersion = '2.5' ext.servletApiVersion = '3.1.0' -ext.springVersion = '4.2.5.RELEASE' +ext.springVersion = '4.3.0.BUILD-SNAPSHOT' repositories { mavenCentral() maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } -} - -compileTestJava { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - options.compilerArgs += '-parameters' + maven { url 'https://repo.spring.io/libs-snapshot' } } dependencies { @@ -56,9 +54,15 @@ dependencies { testCompile("org.junit:junit4-runner:${junit5Version}") } -configurations.all { - // Do NOT cache JUnit 5 snapshot JARs for more than 60 seconds. - resolutionStrategy.cacheChangingModulesFor 60, 'seconds' +compileJava { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 +} + +compileTestJava { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + options.compilerArgs += '-parameters' } junit5 { @@ -75,6 +79,33 @@ test { exclude '**/**' } +javadoc { + options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED + options.author = true + options.header = project.name + options.addStringOption('Xdoclint:none', '-quiet') +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +configurations.all { + // Do NOT cache snapshot JARs for more than 60 seconds. + resolutionStrategy.cacheChangingModulesFor 60, 'seconds' +} + task wrapper(type: Wrapper) { gradleVersion = '2.12' } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..275ee16 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +version = 1.0.0.BUILD-SNAPSHOT diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..676d4b9 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "spring-test-junit5" diff --git a/src/main/java/org/springframework/test/context/junit5/SpringBean.java b/src/main/java/org/springframework/test/context/junit5/SpringBean.java deleted file mode 100644 index 09b72fd..0000000 --- a/src/main/java/org/springframework/test/context/junit5/SpringBean.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v1.0 which - * accompanies this distribution and is available at - * - * http://www.eclipse.org/legal/epl-v10.html - */ - -package org.springframework.test.context.junit5; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.annotation.AliasFor; - -/** - * Annotation used to signal that a single method parameter should be autowired - * by Spring's dependency injection facilities. - * - *
WARNING: {@code @SpringBean} is a temporary solution - * until {@link Autowired @Autowired} is supported on parameters. - * - * @author Sam Brannen - * @since 5.0 - * @see org.springframework.beans.factory.annotation.Autowired - * @see org.springframework.beans.factory.annotation.Qualifier - * @see org.springframework.beans.factory.annotation.Value - */ -@Autowired -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface SpringBean { - - /** - * Alias for {@link Autowired#required}. - */ - @AliasFor(annotation = Autowired.class, attribute = "required") - boolean required() default true; - -} diff --git a/src/main/java/org/springframework/test/context/junit5/SpringExtension.java b/src/main/java/org/springframework/test/context/junit5/SpringExtension.java index 302dd8a..b318804 100644 --- a/src/main/java/org/springframework/test/context/junit5/SpringExtension.java +++ b/src/main/java/org/springframework/test/context/junit5/SpringExtension.java @@ -47,7 +47,6 @@ import org.springframework.test.context.TestContext; import org.springframework.test.context.TestContextManager; import org.springframework.test.context.junit5.support.MethodParameterFactory; -import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.Assert; /** @@ -59,7 +58,6 @@ * * @author Sam Brannen * @since 5.0 - * @see org.springframework.test.context.junit5.SpringBean * @see org.springframework.test.context.junit5.SpringJUnit5Config * @see org.springframework.test.context.junit5.web.SpringJUnit5WebConfig * @see org.springframework.test.context.TestContextManager @@ -131,7 +129,7 @@ public void afterEach(TestExtensionContext context) throws Exception { /** * Supports method parameter injection for parameters of type {@link ApplicationContext} - * (or a sub-type thereof) and parameters annotated with {@link SpringBean @SpringBean}, + * (or a sub-type thereof) and parameters annotated with {@link Autowired @Autowired}, * {@link Qualifier @Qualifier}, or {@link Value @Value}. * * @see #resolve @@ -152,13 +150,12 @@ public boolean supports(Parameter parameter, MethodInvocationContext methodInvoc * *
Provides comprehensive autowiring support for individual method parameters * on par with Spring's dependency injection facilities for autowired fields and - * methods, including support for {@link Qualifier @Qualifier} and {@link Value @Value} - * with support for property placeholders and SpEL expressions in {@code @Value} - * declarations. + * methods, including support for {@link Autowired @Autowired}, + * {@link Qualifier @Qualifier}, and {@link Value @Value} with support for property + * placeholders and SpEL expressions in {@code @Value} declarations. * - *
If the parameter is annotated with {@code @Qualifier}, {@link Qualifier#value} - * will be used as the qualifier for resolving ambiguities; otherwise, the - * name of the parameter will be used as the qualifier. + *
If an explicit qualifier is not declared, the name of the parameter
+ * will be used as the qualifier for resolving ambiguities.
*
* @see #supports
* @see AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor, String)
@@ -167,12 +164,16 @@ public boolean supports(Parameter parameter, MethodInvocationContext methodInvoc
public Object resolve(Parameter parameter, MethodInvocationContext methodInvocationContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
+ Class> testClass = extensionContext.getTestClass();
+
boolean required = findMergedAnnotation(parameter, Autowired.class).map(Autowired::required).orElse(true);
MethodParameter methodParameter = MethodParameterFactory.createSynthesizingMethodParameter(parameter);
DependencyDescriptor descriptor = new DependencyDescriptor(methodParameter, required);
- descriptor.setContainingClass(extensionContext.getTestClass());
- ApplicationContext applicationContext = getApplicationContext(extensionContext.getTestClass());
- return applicationContext.getAutowireCapableBeanFactory().resolveDependency(descriptor, null);
+ descriptor.setContainingClass(testClass);
+
+ ApplicationContext applicationContext = getApplicationContext(testClass);
+ AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
+ return beanFactory.resolveDependency(descriptor, null);
}
// -------------------------------------------------------------------------
@@ -180,6 +181,7 @@ public Object resolve(Parameter parameter, MethodInvocationContext methodInvocat
/**
* Get the {@link TestContextManager} associated with the supplied test class.
* @param testClass the test class to be managed; never {@code null}
+ * @return the {@code TestContextManager}; never {@code null}
*/
private TestContextManager getTestContextManager(Class> testClass) {
Assert.notNull(testClass, "testClass must not be null");
@@ -190,13 +192,13 @@ private TestContextManager getTestContextManager(Class> testClass) {
* Get the {@link ApplicationContext} associated with the supplied test class.
* @param testClass the test class whose context should be retrieved; never {@code null}
* @return the application context
+ * @throws IllegalStateException if an error occurs while retrieving the
+ * application context
+ * @see TestContext#getApplicationContext()
*/
private ApplicationContext getApplicationContext(Class> testClass) {
Assert.notNull(testClass, "testClass must not be null");
- TestContextManager testContextManager = getTestContextManager(testClass);
- // TODO Remove use of reflection once we upgrade to Spring 4.3 RC1 or higher.
- TestContext testContext = (TestContext) ReflectionTestUtils.getField(testContextManager, "testContext");
- return testContext.getApplicationContext();
+ return getTestContextManager(testClass).getTestContext().getApplicationContext();
}
private static Optional findMergedAnnotation(AnnotatedElement element,
diff --git a/src/test/java/org/springframework/test/context/junit5/SpringExtensionTests.java b/src/test/java/org/springframework/test/context/junit5/SpringExtensionTests.java
index d017522..ad54a8c 100644
--- a/src/test/java/org/springframework/test/context/junit5/SpringExtensionTests.java
+++ b/src/test/java/org/springframework/test/context/junit5/SpringExtensionTests.java
@@ -106,13 +106,13 @@ void autowiredFields() {
}
@Test
- void autowiredParameterByTypeForSingleBean(@SpringBean Dog dog) {
+ void autowiredParameterByTypeForSingleBean(@Autowired Dog dog) {
assertNotNull(dog, "Dogbert should have been @Autowired by Spring");
assertEquals("Dogbert", dog.getName(), "Dog's name");
}
@Test
- void autowiredParameterByTypeForPrimaryBean(@SpringBean Cat primaryCat) {
+ void autowiredParameterByTypeForPrimaryBean(@Autowired Cat primaryCat) {
assertNotNull(primaryCat, "Primary cat should have been @Autowired by Spring");
assertEquals("Catbert", primaryCat.getName(), "Primary cat's name");
}
@@ -129,31 +129,31 @@ void autowiredParameterWithExplicitQualifier(@Qualifier("wally") Person person)
* {@code @Qualifier("wally")}.
*/
@Test
- void autowiredParameterWithImplicitQualifierBasedOnParameterName(@SpringBean Person wally) {
+ void autowiredParameterWithImplicitQualifierBasedOnParameterName(@Autowired Person wally) {
assertNotNull(wally, "Wally should have been @Autowired by Spring");
assertEquals("Wally", wally.getName(), "Person's name");
}
@Test
- void autowiredParameterAsJavaUtilOptional(@SpringBean Optional