Share
Embed
Transcript
73 slides
-
The New Testing Framework for Java and Testing Platform for
the JVM @marcphilipp & @sam_brannen JUnit -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Marc Philipp • Software Engineer
@ LogMeIn in Karlsruhe, Germany • Consultant, trainer, coach in a former life • JUnit Maintainer since 2012 • JUnit’s “Keeper of the Green Bar” for 2016 and 2017 • Twitter: @marcphilipp • GitHub: marcphilipp • Web: marcphilipp.de -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 > 3000 employees > 18
offices worldwide SaaS AWS, Azure microservices Spring Boot Docker DevOps -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Sam Brannen • Spring and
Java Consultant • Trainer, Coach, … • Hardcore developer at heart • Java Developer for about 20 years • Spring Framework Core Committer since 2007 • JUnit 5 Core Committer since October 2015 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Experts in Spring and Enterprise
Java Areas of expertise • Spring * • JUnit 5 • Java EE • Software Architecture • Code Reviews Where you find us • Zurich, Switzerland • @swiftmind • http://www.swiftmind.com -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Agenda • Impetus for Change
• JUnit 5 • Demos • Live Coding • Spring 5 • Q & A -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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 and Java 9 and … -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 JUnit 4 Runner API •
Very powerful • In fact, it can do anything • But… you can’t combine Runners • Parameterized + SpringRunner à no way -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Roadmap • Prototype à December
2nd, 2015 • 5.0.0-ALPHA à February 1st, 2016 • Milestones & RCs à July 2016 – August 2017 • 5.0.0 GA à September 10th, 2017 ✅ • 5.0.1 à October 3rd, 2017 ✅ • 5.0.2 & 5.1 M1 à soon -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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) -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 JUnit 5 – Java Versions
• Java 8 • baseline • but can be used to test application code compiled against previous JDK versions • Java 9 • #WorksFineOnJDK9 • every artifact has a stable AUTOMATIC-MODULE-NAME • module-path scanning support coming in 5.1 • Java 10 • We just started building against EA builds last week -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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 Revolutionary Evolutionary Necessary -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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 • ConsoleLauncher for command-line support -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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… -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Third-party TestEngines • Specsy •
Spek • Cucumber • Drools Scenario • jqwik source: https://github.com/junit-team/junit5/wiki/Third-party-Extensions -
P L A T F O R M J U
P I T E R V I N T A G E P A R T Y T H I R D -
P L A T F O R M J U
P I T E R V I N T A G E P A R T Y T H I R D -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 IDEs and Build Tools •
IntelliJ: ✅ IDEA 2016.2+ (2017.2.5) • Eclipse: ✅ Eclipse Oxygen 4.7.1a • NetBeans: • Gradle: interim solution from JUnit Team • to be taken over by Gradle team (end of 2017) • Android JUnit 5: third-party Android support • Maven: interim solution from JUnit Team • being taken over by Maven Surefire team See user guide and sample apps for examples -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 JUnit Jupiter – 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 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Extension APIs • BeforeAllCallback •
BeforeEachCallback • BeforeTestExecutionCallback • AfterTestExecutionCallback • AfterEachCallback • AfterAllCallback • TestExecutionExceptionHandler • ExecutionCondition • TestInstancePostProcessor • ParameterResolver • TestTemplateInvocationContextProvider Lifecycle Callbacks Dependency Injection -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 JUnit Jupiter – Programming Model
org.junit.jupiter.api (org.junit.jupiter.params) • 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 • Repeated tests, parameterized tests, dynamic tests -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Annotations • @Test / @TestFactory
and @Testable for TestEngine implementors • @RepeatedTest / @ParameterizedTest and @TestTemplate • @Nested • @TestInstance • @BeforeAll / @AfterAll • @BeforeEach / @AfterEach • @DisplayName • @Tag • @Disabled -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 JUnit 4 Rule Migration Support
• @EnableRuleMigrationSupport o located in experimental junit-jupiter-migrationsupport module o registers 3 extensions for JUnit Jupiter • ExternalResourceSupport o TemporaryFolder, etc. • VerifierSupport o ErrorCollector, etc. • ExpectedExceptionSupport o ExpectedException o minor bugs in 5.0.0; fixed in 5.0.1and 5.0.2 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Assertions org.junit.jupiter.api.Assertions • Limited set
of core assertions • assertEquals(), assertNotNull(), etc. • assertThrows() – λ • assertTimeout() and assertTimeoutPreemptively() – λ • assertAll() – λ • Supplier<String> à λ for lazy failure message evaluation • message is now the last parameter • For more power, use AssertJ, Hamcrest, etc. -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Assumptions org.junit.jupiter.api.Assumptions • Limited set
of core assumptions • For aborting tests mid-flight • Otherwise, favor a custom ExecutionCondition for skipping • assumeTrue() / assumeFalse() • BooleanSupplier, Supplier<String> – λ • assumingThat( ? , () -> {} ); – λ -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 LIVE CODING DEMO from JUnit
4 to JUnit Jupiter -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Tagging @Tag("fast") @Test void myFastTest()
{ } • Declare @Tag on a test interface, class, or method -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Custom Tags @Target(METHOD) @Retention(RUNTIME) @Tag("fast")
public @interface Fast { } • Declare @Tag as a meta-annotation @Fast @Test void myFastTest() {} -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Composed Tags @Target(METHOD) @Retention(RUNTIME) @Tag("fast")
@Test public @interface FastTest { } • Declare @Tag as a meta-annotation with other annotations (JUnit, Spring, etc.) @FastTest void myFastTest() { } -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Dependency Injection • Extension Model
meets Programming Model • ParameterResolver extension API • resolves parameters for @Test and lifecycle methods o and even constructors • can register multiple simultaneously • only one wins • Use cases • server URL, DataSource, Spring ApplicationContext, etc. -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 TestInfo • TestInfo: inject into
constructor, @Test, @BeforeEach, etc. • access display name, tags, class, method • TestInfoParameterResolver • eating our own dog food ;-) • See also: • RepetitionInfo for @RepeatedTest • TestReporter • MockitoExtension • SpringExtension -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Conditional Test Execution • Extension
Model meets Programming Model • ExecutionCondition • @Disabled • DisabledCondition • eating our own dog food ;-) • Deactivate via Launcher, system property, or junit-platform.properties file • junit.conditions.deactivate = org.junit.* Game Changer -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 LIVE CODING DEMO tags, display
names, dependency injection, & conditions -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Interface Default Methods • Enables
the concept of a test interface • leading to multiple inheritance in tests • a.k.a., testing traits • @BeforeAll / @AfterAll • if using @TestInstance(Lifecyle.PER_CLASS) • @BeforeEach / @AfterEach • @Test / @RepeatedTest / @ParameterizedTest / @TestFactory • @Tag • @ExtendWith • See StringTests and TestInterfaceDemo examples in user guide -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 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 and Bowling Game Kata by Tim Riemer -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Repeated Tests • Annotate a
method with @RepeatedTest instead of @Test o and specify the number of repetitions • Optionally have the RepetitionInfo injected as a method parameter • Optionally override the display name -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 @RepeatedTest in Action @RepeatedTest(5) void
repeatedTest(RepetitionInfo repetitionInfo) { assertEquals(5, repetitionInfo.getTotalRepetitions()); } @RepeatedTest( value = 5, name = "Wiederholung {currentRepetition} von {totalRepetitions}” ) void repeatedTestInGerman() { // ... } -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Parameterized Tests (junit-jupiter-params) • Annotate
a method with @ParameterizedTest instead of @Test o and specify the source of the arguments o optionally override the display name • Sources o @ValueSource: String, int, long, double o @EnumSource o @MethodSource o @CsvSource & @CsvFileSource o @ArgumentsSource & custom ArgumentsProvider -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Argument Conversion • Implicit conversion
o Primitive types and their wrappers o Enums o java.time types (JSR-310) • Explicit conversion o @ConvertWith and custom ArgumentConverter o @JavaTimeConversionPattern built-in support for JSR-310 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 @ParameterizedTest in Action @ParameterizedTest @ValueSource(strings
= { "mom", "dad", "radar", "racecar", "able was I ere I saw elba" }) void palindromes(String candidate) { assertTrue(isPalindrome(candidate)); } -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Dynamic Tests • Conventional tests
are static (i.e., known at compile time) • @Test • A DynamicTest is registered at runtime – λ • as lambda expression in a stream, collection, etc. • by a method annotated with @TestFactory • Can also register a DynamicContainer for dynamic nesting • Somewhat analogous to parameterized tests • just more… dynamic -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Dynamic Tests in Action @TestFactory
Stream<DynamicTest> dynamicTestsFromIntStream() { // Generates tests for the first 10 even integers. return IntStream.iterate(0, n -> n + 2) .limit(10) .mapToObj(n -> dynamicTest("test" + n, () -> assertTrue(n % 2 == 0))); } -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 What’s Missing? • Scenario tests
• Ordering • Parallel execution • Execution in user-defined thread • Declarative and programmatic test suites for the JUnit Platform • Java 9 module-path scanning • … -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 LIVE CODING DEMO custom extensions
• logging • timing • conditions -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Spring Support for JUnit Jupiter
• Fully integrated in Spring Framework 5.0! • Supports all Core Spring TestContext Framework features • Constructor and method injection via @Autowired, @Qualifier, @Value • Conditional test execution via SpEL expressions • ApplicationContext configuration annotations • Also works with Spring Framework 4.3 https://github.com/sbrannen/spring-test-junit5 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Configuring JUnit Jupiter with Spring
• SpringExtension • @ExtendWith(SpringExtension.class) • @SpringJUnitConfig • @ContextConfiguration + SpringExtension • @SpringJUnitWebConfig • @SpringJUnitConfig + @WebAppConfiguration • @EnabledIf / @DisabledIf • SpEL expression evaluation for conditional execution -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 DEMO Spring 5 and JUnit
Jupiter @EnabledOnMac / @DisabledOnMac -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Spring Boot 1.5 + JUnit
5 – Custom Config @Target(TYPE) @Retention(RUNTIME) @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = MOCK) @AutoConfigureMockMvc @Transactional public @interface SpringEventsWebTest { } • @SpringBootTest + @AutoConfigureMockMvc + @ExtendWith(SpringExtension.class) -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Spring Boot 1.5 + JUnit
5 – MockMvc Test @SpringEventsWebTest class 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 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 JUnit 5 Resources Project Homepage
à http://junit.org/junit5 User Guide à http://junit.org/junit5/docs/current/user-guide Javadoc à http://junit.org/junit5/docs/current/api GitHub à https://github.com/junit-team Gitter à https://gitter.im/junit-team/junit5 Stack Overflow à http://stackoverflow.com/tags/junit5 -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Spring Resources Spring Framework à
http://projects.spring.io/spring-framework Spring Guides à http://spring.io/guides Spring JIRA à https://jira.spring.io Spring on GitHub à https://github.com/spring-projects/spring-framework Stack Overflow à spring, spring-test, spring-mvc, spring-boot, … -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Demos Used in this Presentation
https://github.com/sbrannen/junit5-demo -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 JUnit 5 BOF @ Devoxx
2017 In 20 minutes in BOF room #2 (19:00 – 20:00) -
@marcphilipp & @sam_brannen #Devoxx #JUnit5 Q & A Marc Philipp
Sam Brannen JUnit