Slide 1

Slide 1 text

5 JUNIT 5 EXTENSIONS MARC PHILIPP

Slide 2

Slide 2 text

5 MARC PHILIPP So ware Engineer at JUnit Maintainer since 2012 Twi er: Web: @marcphilipp marcphilipp.de

Slide 3

Slide 3 text

5 JUNIT 5 IS HERE! 5.0 September 10, 2017 5.1 February 18, 2018 5.2 April 29, 2018

Slide 4

Slide 4 text

5 ✋ SHOW OF HANDS

Slide 5

Slide 5 text

5 J U N I T 5 P L AT F O R M Another Test P A R T Y T H I R D J U P I T E R V I N TA G E

Slide 6

Slide 6 text

5 JUNIT JUPITER P L AT F O R M J U P I T E R

Slide 7

Slide 7 text

5 JUNIT JUPITER API programming model for test authors extension model for extension authors

Slide 8

Slide 8 text

5 PROGRAMMING MODEL import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; class SimpleTest { @Test @DisplayName("1 + 1 = 2") void onePlusOneEqualsTwo() { assertEquals(2, 1 + 1); } }

Slide 9

Slide 9 text

5 EXTENSION MODEL import org.junit.jupiter.api.extension.*; class MyCustomExtension implements BeforeEachCallback, AfterEachCallback { @Override public void beforeEach(ExtensionContext context) { // setup } @Override public void afterEach(ExtensionContext context) { // teardown } }

Slide 10

Slide 10 text

5 EXTENSION REGISTRATION Declara ve: @ExtendWith on classes or methods Programma c: @RegisterExtension on fields Global: via ServiceLoader You can register as many extensions as you need simultaneously

Slide 11

Slide 11 text

5 PROGRAMMING MODEL MEETS EXTENSION MODEL import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.*; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(MyCustomExtension.class) class SimpleTest { @RegisterExtension Extension myExtension = new AnotherExtension(42); @Test @ExtendWith({FooExtension.class, BarExtension.class}) void onePlusOneEqualsTwo() { assertEquals(2, 1 + 1); } }

Slide 12

Slide 12 text

5 CONDITIONAL TEST EXECUTION

Slide 13

Slide 13 text

5 DEMO h ps:/ /github.com/marcphilipp/junit5‑extensions‑demo

Slide 14

Slide 14 text

5 EXTENSION CONTEXT Represents the current node in the test tree, e.g. test method or class Provides access to meta informa on about such a node, e.g. display name, method, class

Slide 15

Slide 15 text

5 EXTENSION CONTEXT root : ExtensionContext displayName = "JUnit Jupiter" parent class1 : ExtensionContext displayName = "TestClass1" parent test1 : ExtensionContext displayName = "test1()" parent test2 : ExtensionContext displayName = "test2()" parent class2 : ExtensionContext displayName = "TestClass2" parent test3 : ExtensionContext displayName = "test3()"

Slide 16

Slide 16 text

5 LESSONS LEARNED Using custom logic to determine whether a test class/method should be skipped Registering an extension globally Deac va ng a condi on without changing the code

Slide 17

Slide 17 text

5 REUSABLE TEST SETUP & TEARDOWN

Slide 18

Slide 18 text

5 DEMO h ps:/ /github.com/marcphilipp/junit5‑extensions‑demo

Slide 19

Slide 19 text

5 WHY THE STORE ABSTRACTION? An extension needs to save and retrieve data, e.g. to clean up in the end Extensions are instan ated once and called for mul ple tests Using instance variables would be error‑prone extensionContext .getStore(Namespace.create(...)) .getOrComputeIfAbsent("key", key -> ...)

Slide 20

Slide 20 text

5 STORE Map ‑like interface for extensions to save and retrieve data, e.g. store.put(key, value) Accessed via a Namespace : Enables sharing data across extensions, but makes it a deliberate decision (e.g. Namespace.GLOBAL ) Reading from a Store follows the hierarchy upwards, if a key is not found

Slide 21

Slide 21 text

5 STORE root : ExtensionContext displayName = "JUnit Jupiter" parent class1 : ExtensionContext displayName = "TestClass1" parent store test1 : ExtensionContext displayName = "test1()" a : Store + get(Object, Class): T, ... parent b : Store + get(Object, Class): T, ... parent c : Store + get(Object, Class): T, ... Namespace store Namespace store Namespace

Slide 22

Slide 22 text

5 AUTOMATIC CLEAN‑UP CloseableResource instances in a Store are automa cally closed when the scope of the corresponding ExtensionContext ends. class DockerClientResource implements CloseableResource { private final DockerClient dockerClient; DockerClientResource() { var config = DefaultDockerClientConfig.createDefaultConfigBuilder dockerClient = DockerClientBuilder.getInstance(config).build(); } DockerClient get() { return dockerClient; } @Override public void close() throws Throwable { dockerClient.close(); } }

Slide 23

Slide 23 text

5 LESSONS LEARNED Using the Store class for extension state Using Lifecycle Callbacks to enable reuse of common setup/teardown code Implemen ng mul ple Extension interfaces in a single extension

Slide 24

Slide 24 text

5 RESOLVING TEST PARAMETERS

Slide 25

Slide 25 text

5 DEMO h ps:/ /github.com/marcphilipp/junit5‑extensions‑demo

Slide 26

Slide 26 text

5 LESSONS LEARNED How to resolve test method parameters in an Extension ? You can also inject parameters into test class constructors and @BeforeEach / @AfterEach methods etc.

Slide 27

Slide 27 text

5 PROVIDING ARGUMENTS FOR PARAMETERIZED TESTS

Slide 28

Slide 28 text

5 DEMO h ps:/ /github.com/marcphilipp/junit5‑extensions‑demo

Slide 29

Slide 29 text

5 LESSONS LEARNED Using Parameterized Tests Wri ng a custom ArgumentsProvider that loads data from a JSON file, …

Slide 30

Slide 30 text

5 FROM TESTS TO TEST TEMPLATES

Slide 31

Slide 31 text

5 DEMO h ps:/ /github.com/marcphilipp/junit5‑extensions‑demo

Slide 32

Slide 32 text

5 LESSONS LEARNED Execu ng a test mul ple mes with different contexts Implemen ng a TestInvocationContextProvider

Slide 33

Slide 33 text

5 SUPPORT CLASSES Package org.junit.platform.commons.support contains: AnnotationSupport to scan for custom annota ons, including meta‑annota ons ReflectionSupport for classpath scanning, finding methods, invoking them etc.

Slide 34

Slide 34 text

5 SUMMARY

Slide 35

Slide 35 text

5 EXTENSION POINTS Lifecycle: BeforeAllCallback , BeforeEachCallback ✅, BeforeTestExecutionCallback , TestExecutionExceptionHandler , AfterTestExecutionCallback , AfterEachCallback ✅, AfterAllCallback Other: ExecutionCondition ✅, TestInstancePostProcessor , ParameterResolver ✅, TestTemplateInvocationContextProvider ✅

Slide 36

Slide 36 text

5 JUNIT JUPITER IS EXTENSIBLE A lot of extension points to choose from The JUnit team will add more in future releases Combining mul ple extension points in one extension is very powerful!

Slide 37

Slide 37 text

5 THIRD‑PARTY EXTENSIONS Spring, Mockito, Docker, Wiremock, JPA, Selenium/WebDriver, DbUnit, Ka a, Jersey, GreenMail, S3Mock, Citrus Framework, XWiki, … h ps:/ /github.com/junit‑team/junit5/wiki/Third‑party‑ Extensions

Slide 38

Slide 38 text

5 GETTING STARTED User Guide: Sample projects for Gradle, Maven, and Ant: Javadoc: h p:/ /junit.org/junit5/docs/current/user‑guide/ h ps:/ /github.com/junit‑team/junit5‑samples h p:/ /junit.org/junit5/docs/current/api/

Slide 39

Slide 39 text

5 WANTED: FEEDBACK! StackOverflow: Code & Issues: Twi er: h p:/ /stackoverflow.com/ques ons/tagged/junit5 h ps:/ /github.com/junit‑team/junit5/ h ps:/ /twi er.com/juni eam

Slide 40

Slide 40 text

5 EXAMPLE CODE h ps:/ /github.com/marcphilipp/junit5‑extensions‑demo

Slide 41

Slide 41 text

5 QUESTIONS? or @marcphilipp @juni eam

Slide 42

Slide 42 text

5 THANKS!