Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JUnit 5 Extensions

Marc Philipp
November 03, 2017

JUnit 5 Extensions

JUnit 5 has finally arrived! It introduces a completely new extension model that allows to customize almost every aspect of test execution.

For example, it lets you define custom conditions to decide whether a test should be executed or skipped. Test lifecycle callbacks allow to encapsulate common setup/teardown code in an extension. An extension can pass values or inject dependencies to a test by post-processing test instances, or resolving test method parameters. Moreover, you can even write an extension that specifies how a test method becomes a template for multiple tests and how to invoke those, e.g. multiple times with different parameters or a different setup.

In this talk, we will go on an example-driven tour of the new extension API using real-world testing scenarios. We will learn about the utilities an extension can use, e.g. how it should store state, so you will be ready to write your own extensions after this session.

Marc Philipp

November 03, 2017
Tweet

More Decks by Marc Philipp

Other Decks in Programming

Transcript

  1. 5 MARC PHILIPP Software Engineer @ LogMeIn in Karlsruhe, Germany

    JUnit Maintainer since 2012 Twitter: @marcphilipp Web: marcphilipp.de 1 . 2
  2. 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 2 . 2
  3. 5 JUNIT JUPITER P L AT F O R M

    J U P I T E R 2 . 3
  4. 5 PROGRAMMING MODEL import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; class SimpleTest

    { @Test @DisplayName("1 + 1 = 2") @ExtendWith(MyCustomExtension.class) void onePlusOneEqualsTwo() { assertEquals(2, 1 + 1); } } 2 . 5
  5. 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 } } 2 . 6
  6. 5 EXTENSION REGISTRATION Declarative: Annotate your test classes or methods

    with @ExtendWith to register extensions May be used as a meta-annotation Global: Opt-in support for registration via ServiceLoader 2 . 7
  7. 5 EXTENSION CONTEXT Represents the current node in the test

    tree, e.g. test method or class Provides access to meta information about such a node, e.g. display name, method, class 3 . 3
  8. 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()" 3 . 4
  9. 5 LESSONS LEARNED How to implement custom logic to determine

    whether a test class/method should be skipped? How to use global extension registration? How to deactivate a condition without changing the code using system properties? 3 . 5
  10. 5 WHY THE STORE ABSTRACTION? Methods within an extension need

    to save and retrieve data, e.g. to cleanup in the end Extensions are instantiated once and called for multiple tests Using instance variables would be error-prone 4 . 3
  11. 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 4 . 4
  12. 5 STORE root : ExtensionContext displayName = "JUnit Jupiter" parent

    class1 : ExtensionContext displayName = "TestClass1" parent store test1 : ExtensionContext displayName = "test1()" a : Store + get(Object, Class<T>): T, ... parent b : Store + get(Object, Class<T>): T, ... parent c : Store + get(Object, Class<T>): T, ... Namespace store Namespace store Namespace 4 . 5
  13. 5 LESSONS LEARNED Using the Store class for extension state

    Using Lifecycle Callbacks to enable reuse of common setup/teardown code Implementing multiple Extension interfaces in a single extension 4 . 6
  14. 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 5 . 3
  15. 5 LESSONS LEARNED How to use Parameterized Tests? Writing a

    custom ArgumentsProvider that loads data from a JSON le, … 6 . 3
  16. 5 LESSONS LEARNED How to execute a test multiple times

    with di erent contexts? How to implement a TestInvocationContextProvider 7 . 3
  17. 5 SUPPORT CLASSES Package org.junit.platform.commons.support contains: AnnotationSupport to scan for

    custom annotations, including meta-annotations ReflectionSupport for classpath scanning, nding methods, invoking them etc. 7 . 4
  18. 5 JUNIT JUPITER IS EXTENSIBLE A lot of extension points

    to choose from The JUnit team will add more in future releases Combining multiple extension points in one extension is very powerful! 8 . 2
  19. 5 GETTING STARTED User Guide: Sample projects for Gradle and

    Maven: Javadoc: http://junit.org/junit5/docs/current/user-guide/ https://github.com/junit-team/junit5-samples http://junit.org/junit5/docs/current/api/ 8 . 3
  20. 5 WANTED: FEEDBACK! StackOver ow: Code & Issues: Twitter: http://stackover

    ow.com/questions/tagged/junit5 https://github.com/junit-team/junit5/ https://twitter.com/junitteam 8 . 4