testing with spring 4.3, junit 5, and beyond
TRANSCRIPT
Testing with Spring 4.3,JUnit 5, and beyond ...
Sam Brannen@sam_brannen
Sam Brannen• Spring and Java Consultant @
• Java Developer for over 17 years
• Spring Framework Core Committer since 2007
• Swiss Spring User Group Lead
• Trainer & Conference Speaker
• JUnit 5 Core Committer since October 2015
3
Your experts for Spring and Enterprise Java
Areas of expertise• Spring *• JUnit• Java EE• Software Architecture• Code Reviews
Where you find us• Zurich, Switzerland• @swiftmind• http://
www.swiftmind.com
Agenda• Spring Events App
• Spring 4.3
• JUnit 5
• Spring 5
• Q & A
4
Show of hands …
5
Spring Events App
6
The Spring Events Example Application• Spring Boot powered web app
• Spring Boot 1.4, Spring Framework 5, JUnit 5
• Simple POJO domain model: Event
• Transactional service layer
• Spring Data JPA repository layer
• Spring @MVC + Thymeleaf & REST presentation layer
• Spring Security
• https://github.com/sbrannen/spring-events
7
New in Spring Framework 4.3
8
Odds & Ends• JUnit 4.12+ required by the Spring TestContext Framework (TCF)
• Support for primary transaction managers and data sources in the TCF
• @Sql and @SqlGroup may now be used as meta-annotations• to create custom composed annotations with attribute overrides• same is true for many @Repeatable annotations in Core Spring
• ReflectionTestUtils automatically unwraps proxies when setting/getting a field
9
Extending the Spring TestContext Framework• The getTestContext() method in TestContextManager is now public
• New ContextCustomizer and ContextCustomizerFactory SPIs• Potentially replaces need for custom ContextLoader• Customize ApplicationContext after bean definitions are loaded
but before the context is refreshed• Registered globally by third parties via spring.factories mechanism• Enables Spring Boot Test magic• Implement to create your own magic
10
New Features• SpringRunner alias for the SpringJUnit4ClassRunner
• @RunWith(SpringRunner.class)
• @ContextConfiguration can be completely omitted…• if default XML, Groovy, or @Configuration is detected• Spring Boot Test 1.4 even locates your @SpringBootApplication class
• ApplicationContext cache is now bounded• default maximum size of 32• Least Recently Used eviction policy• configured via spring.test.context.cache.maxSize JVM/Spring
property11
Demo
12
SpringRunner, @ContextConfiguration, bounded cache
Preparing for the Future• Testing Traits
• Spring test annotations can be declared on interfaces• Combines nicely with Java 8 default methods and JUnit 5• Enabling so called testing traits in Java
• Non-public @Transactional test methods• For use with JUnit 5 and TestNG
• Non-public @BeforeTransaction and @AfterTransaction methods• can also be declared on default methods to create testing traits
13
MockMvc Improvements• Expectations on multi-value response headers
• HeaderResultMatchers.stringValues(String, String...)
• Form data request content (e.g., from a POST) is now parsed and used to populate request parameters
• Support for custom HTTP verbs (e.g., WebDAV)• MockMvcRequestBuilders.request(String methodName, URI uri)
• Improved Cookie support for HtmlUnit integration
14
MockMvc – Assert Invoked Handler Method• New mock-like methodCall() assertion in HandlerResultMatchers
• Assert the @Controller method invoked to handle the response• Uses MvcUriComponentsBuilder under the hood
15
mockMvc.perform(get("/")) .andExpect(handler().methodCall( on(HomeController.class).showHomePage()));
MockMvc – JSON Prefixes• New support for stripping JSON prefixes from responses
• a prefix is used to prohibit JSON Hijacking
16
mockMvc.perform(get("/account/42").accept(APPLICATION_JSON)) .andExpect(jsonPath("$.pin") .prefix("&&enigma&&") .value("1234"));
MockRestServiceServer Improvements• Expectations for form data in the request body
• ContentRequestMatchers.formData(MultiValueMap<String, String>)
• Specify expectation counts• Pass ExpectedCount to the expect() method• once(), manyTimes(), times(int), min(int), max(int), between(int, int)• verify() and reset() afterwards
• Specify whether ordering should be ignored• Invoke ignoreExpectOrder(true) on the MockRestServiceServerBuilder • MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
17
JUnit 5
18
Impetus for Change• JUnit 4.0 was released a decade ago
• a lot has changed since then…• testing needs have matured• expectations have grown
• Modularity big ball of mud (i.e., only THE junit.jar)
• Test discovery and execution tightly coupled
• Extensibility lot of room for improvement
• Let’s not forget Java 8
JUnit 4 Runner API• Very powerful
• In fact, it can do anything
• But… you can’t combine Runners
• Parameterized + SpringJUnit4ClassRunner no way
JUnit 4… Rules… are meant to be broken• JUnit 4.7: MethodRule @Rule• JUnit 4.9: TestRule @Rule / @ClassRule
• Great for simple use cases• Can even be combined
• But… a single rule can’t be used for method-level and class-level callbacks
• Plus… zero support for instance-level callbacks
• Case in point: SpringClassRule / SpringMethodRule
JUnit Lambda – Crowdfunding Campaign• Initiated by Johannes Link and Marc Philipp
• Later joined by Matthias Merdes, Stefan Bechtold, & Sam Brannen
• Ran from July to October 2015
• Raised 53,937 Euros from 474 individuals and companies
• 4 companies donated 6 weeks of developer time
Thanks!
The Kick-off Team
JUnit 5 – Roadmap• Prototype December 2nd, 2015
• 5.0.0-ALPHA February 1st, 2016
• 5.0.0-M1 July 7th 2016
• 5.0.0-M2 July 23rd 2016
• M3, M4, RC1 Fall 2016
• GA end of 2016 ... maybe
JUnit 5 – in a Nutshell• Modular
• Extensible
• Modern
• Forward and backward compatible• JUnit Platform supports JUnit 3.8, JUnit 4, and JUnit 5• New testing frameworks can be run with JUnit 4 infrastructure
o @RunWith(JUnitPlatform.class)
JUnit 5 = Platform + Jupiter + Vintage• JUnit Platform 1.0.0
• Foundation for launching testing frameworks on the JVM• Launcher and TestEngine APIs• ConsoleLauncher, Gradle plugin, Maven Surefire provider
• JUnit Jupiter 5.0.0• New programming model and extension model for JUnit 5
• JUnit Vintage 4.12.0• TestEngine for running JUnit 3 and JUnit 4 based tests
Launcher API• Used by IDEs and build tools to launch the framework
• Central API for discovering and executing tests via one or more engines
• LauncherDiscoveryRequest• selectors and filters
• Feedback provided via the TestExecutionListener API
TestEngine API• Test engine discovers and executes tests
• for a particular programming model
• Automatic registration via Java’s ServiceLoader mechanism
• JupiterTestEngine
• VintageTestEngine
• Implement your own…
P L A T F O R M
J U P I T E RV I N T A G E P A R T YT H I R D
PL
AT
FO
RM
JU
PI
TE
R
VI
NT
AG
E
PA
RT
YT
HI
RD
JUnit 5 – Extension Model• Extension
• marker interface
• org.junit.jupiter.api.extension• package containing all extension APIs• implement as many as you like
• @ExtendWith(...)• used to register one or more extensions• interface, class, or method level
o or as a meta-annotation
Extension APIs• BeforeAllCallback
• BeforeEachCallbacko BeforeTestExecutionCallbacko AfterTestExecutionCallback
• AfterEachCallback• AfterAllCallback
• ContainerExecutionCondition & TestExecutionCondition• TestInstancePostProcessor• ParameterResolver• TestExecutionExceptionHandler
JUnit 5 – Programming Modelorg.junit.jupiter.api
• Annotations and meta-annotations• Assertions and Assumptions• Custom display names• Visibility• Tagging• Conditional test execution• Dependency injection for constructors and methods• Lambda expressions and method references• Interface default methods• Nested test classes• Dynamic tests
Annotations• @Test / @TestFactory• @BeforeAll / @AfterAll• @BeforeEach / @AfterEach
• @DisplayName
• @Tag
• @Disabled
• @Nested
Assertionsorg.junit.jupiter.api.Assertions
• Limited set of core assertions• assertEquals(), assertNotNull(), etc.• assertThrows() and expectThrows()• assertTimeout()• assertAll()
• Supplier<String> for lazy failure message evaluation• message is now the last parameter
• For more power, use AssertJ, Hamcrest, etc.
Assumptionsorg.junit.jupiter.api.Assumptions
• Limited set of core assumptions• For aborting tests mid-flight
• assumeTrue() / assumeFalse()• BooleanSupplier, Supplier<String>
• assumingThat( ? , () -> {} );
Demo
38
basic tests and assertions
Test Names• Names default to test class or test method names
• characters limited based on Java syntax
• Custom display names @DisplayName• Can contain spaces, special chars, and even emoji 😱
Dependency Injection• Extension Model meets Programming Model
• ParameterResolver extension• resolves parameters for constructors or methods
• TestInfo: inject into constructor, @Test, @BeforeEach, etc.• access display name, tags, class, method
• TestInfoParameterResolver• eating our own dog food ;-)
• See also:• TestReporter• MockitoExtension• SpringExtension
Demo
41
display names and dependency injection
Tagging
@Tag("fast")@Testvoid myFastTest() {}
• Declare @Tag on a test interface, class, or method
Custom Tags
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Tag("fast")@Testpublic @interface FastTest {}
• Declare @Tag as a meta-annotation
@FastTestvoid myFastTest() {}
Conditional Test Execution• Extension Model meets Programming Model
• ContainerExecutionCondition• TestExecutionCondition
• @Disabled• DisabledCondition
• eating our own dog food ;-)
• Deactivate via Launcher/System property• junit.conditions.deactivate = org.junit.*
Interface Default Methods• Introduces the concept of a test interface
• Enables multiple inheritance in tests• a.k.a., testing traits
• @BeforeEach / @AfterEach• @Test• @Tag• @ExtendWith
• See StringTests example in user guide
Nested Test Classes• Enables logical, hierarchical grouping of test classes
• with shared initialization and state from outer classes
• Declare @Nested on non-static nested classes• i.e., inner classes
• You can even combine nested classes and test interfaces
• See TestingAStack example in user guide
Dynamic Tests• Conventional tests are static (i.e., known at compile time)
o @Test methods
• A DynamicTest is registered at run timeo as lambda expression in a stream, collection, etc.o by a method annotated with @TestFactory
• Somewhat analogous to parameterized tests
Demo
48
dynamic tests
What’s Missing?• Official IDE and build integration
• IntelliJ IDEA 2016.2: beta support• Eclipse: on the radar, but not until 2017• Gradle & Maven: interim solutions from JUnit Team
• Parameterized tests
• Scenario tests
• Parallel execution
• …
New in Spring Framework 5.0
50
Spring Support for JUnit 5• Fully integrated in Spring Framework 5.0 M1
• Supports all Core Spring TestContext Framework features• Constructor and method injection via @Autowired, @Qualifier,
@Value
• Also works with Spring Framework 4.3• https://github.com/sbrannen/spring-test-junit5
• SpringExtension• @ExtendWith(SpringExtension.class)
• @SpringJUnitConfig and @SpringJUnitWebConfig
Demo
52
Spring 5 and JUnit 5
Spring Boot 1.4 + JUnit 5 – Custom Config
@Target(TYPE)@Retention(RUNTIME)
@ExtendWith(SpringExtension.class)@SpringBootTest(webEnvironment = MOCK)@AutoConfigureMockMvc@Transactionalpublic @interface SpringEventsWebTest {}
• @SpringBootTest + @AutoConfigureMockMvc + @ExtendWith(SpringExtension.class)
Spring Boot 1.4 + JUnit 5 – MockMvc Test
@SpringEventsWebTestclass EventsControllerTests {
@Test @DisplayName("Home page should display more than 10 events") void listEvents(@Autowired MockMvc mockMvc) throws Exception { mockMvc.perform(get("/")) .andExpect(view().name("event/list")) .andExpect(model().attribute("events", hasSize(greaterThan(10)))); }}
• @SpringEventsWebTest + method-level DI + MockMvc
In closing …
55
Spring ResourcesSpring Frameworkhttp://projects.spring.io/spring-framework
Spring Guideshttp://spring.io/guides
Spring JIRAhttps://jira.spring.io
Spring on GitHubhttps://github.com/spring-projects/spring-framework
Stack Overflowspring, spring-test, spring-mvc, spring-boot, spring-security, …
56
JUnit 5 ResourcesProject Homepagehttp://junit.org/junit5
User Guidehttp://junit.org/junit5/docs/current/user-guide
Javadochttp://junit.org/junit5/docs/current/api
GitHubhttps://github.com/junit-team/junit5https://github.com/junit-team/junit5-samples
Stack Overflowhttp://stackoverflow.com/tags/junit5
57
Blogs
Spring Blog http://spring.io/blog
Swiftmind Blog http://www.swiftmind.com/blog
58
Q & ASam Brannen
@Sam_Brannenslideshare.net/sbrannen
@SpringCentralspring.io
@JUnitTeamjunit.org/junit5