Skip to content

Commit

Permalink
Upgrade to Spring 4.3.0.BUILD-SNAPSHOT
Browse files Browse the repository at this point in the history
In order to align with new features coming in Spring 4.3, this commit
upgrades the Spring dependency from 4.2.5 to 4.3.0.BUILD-SNAPSHOT.

- @Autowired can now be declared directly on method parameters.
- @SpringBean is now obsolete and has been completely removed.
- The TestContext no longer needs to be looked up via reflection, since
  TestContextManager.getTestContext() is now public.

In addition, this commit overhauls the README and build.

- spring-test-junit5 artifacts can now installed in a local Maven
  repository.
- Generated artifacts now include Javadoc and Source JARs.
  • Loading branch information
sbrannen committed Mar 19, 2016
1 parent c724c69 commit 886c64a
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 98 deletions.
75 changes: 63 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,29 +21,79 @@ 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 ]
```

# Running Tests in the IDE

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
51 changes: 41 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,28 @@ buildscript {
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'maven'
apply plugin: 'org.junit.gen5.gradle'

jar {
baseName = 'spring-test-junit5'
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 {
Expand All @@ -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 {
Expand All @@ -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'
}
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version = 1.0.0.BUILD-SNAPSHOT
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "spring-test-junit5"

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -152,13 +150,12 @@ public boolean supports(Parameter parameter, MethodInvocationContext methodInvoc
*
* <p>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.
*
* <p>If the parameter is annotated with {@code @Qualifier}, {@link Qualifier#value}
* will be used as the <em>qualifier</em> for resolving ambiguities; otherwise, the
* name of the parameter will be used as the <em>qualifier</em>.
* <p>If an explicit <em>qualifier</em> is not declared, the name of the parameter
* will be used as the <em>qualifier</em> for resolving ambiguities.
*
* @see #supports
* @see AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor, String)
Expand All @@ -167,19 +164,24 @@ 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);
}

// -------------------------------------------------------------------------

/**
* 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");
Expand All @@ -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 <A extends Annotation> Optional<A> findMergedAnnotation(AnnotatedElement element,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand All @@ -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<Dog> dog) {
void autowiredParameterAsJavaUtilOptional(@Autowired Optional<Dog> dog) {
assertNotNull(dog, "Optional dog should have been @Autowired by Spring");
assertTrue(dog.isPresent(), "Value of Optional should be 'present'");
assertEquals("Dogbert", dog.get().getName(), "Dog's name");
}

@Test
void autowiredParameterThatDoesNotExistAsJavaUtilOptional(@SpringBean Optional<Number> number) {
void autowiredParameterThatDoesNotExistAsJavaUtilOptional(@Autowired Optional<Number> number) {
assertNotNull(number, "Optional number should have been @Autowired by Spring");
assertFalse(number.isPresent(), "Value of Optional number should not be 'present'");
}

@Test
void autowiredParameterThatDoesNotExistButIsNotRequired(@SpringBean(required = false) Number number) {
void autowiredParameterThatDoesNotExistButIsNotRequired(@Autowired(required = false) Number number) {
assertNull(number, "Non-required number should have been @Autowired as 'null' by Spring");
}

@Test
void autowiredParameterOfList(@SpringBean List<Person> peopleParam) {
void autowiredParameterOfList(@Autowired List<Person> peopleParam) {
assertNotNull(peopleParam, "list of people should have been @Autowired by Spring");
assertEquals(2, peopleParam.size(), "Number of people in context");
}
Expand Down Expand Up @@ -188,7 +188,7 @@ void valueParameterFromSpelExpressionWithNestedPropertyPlaceholder(@Value("#{'He
}

@Test
void junitAndSpringMethodInjectionCombined(@SpringBean Cat kittyCat, TestInfo testInfo, ApplicationContext context,
void junitAndSpringMethodInjectionCombined(@Autowired Cat kittyCat, TestInfo testInfo, ApplicationContext context,
TestReporter testReporter) {

assertNotNull(testInfo, "TestInfo should have been injected by JUnit");
Expand Down
Loading

0 comments on commit 886c64a

Please sign in to comment.