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
    JUNIT 5 EXTENSIONS
    1 . 1

    View Slide

  2. 5
    MARC PHILIPP
    Software Engineer @ LogMeIn
    in Karlsruhe, Germany
    JUnit Maintainer since 2012
    Twitter: @marcphilipp
    Web: marcphilipp.de
    1 . 2

    View Slide

  3. 5
    JUNIT 5 IS RELEASED!
    Release date: September 10, 2017
    2 . 1

    View Slide

  4. 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

    View Slide

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

    View Slide

  6. 5
    JUNIT JUPITER API
    programming model for test authors
    extension model for extension authors
    2 . 4

    View Slide

  7. 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

    View Slide

  8. 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

    View Slide

  9. 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

    View Slide

  10. 5
    CONDITIONAL TEST
    EXECUTION
    3 . 1

    View Slide

  11. 5
    DEMO
    3 . 2

    View Slide

  12. 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

    View Slide

  13. 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

    View Slide

  14. 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

    View Slide

  15. 5
    REUSABLE TEST
    SETUP & TEARDOWN
    4 . 1

    View Slide

  16. 5
    DEMO
    4 . 2

    View Slide

  17. 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

    View Slide

  18. 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

    View Slide

  19. 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
    4 . 5

    View Slide

  20. 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

    View Slide

  21. 5
    RESOLVING TEST
    PARAMETERS
    5 . 1

    View Slide

  22. 5
    DEMO
    5 . 2

    View Slide

  23. 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

    View Slide

  24. 5
    PROVIDING
    ARGUMENTS FOR
    PARAMETERIZED
    TESTS
    6 . 1

    View Slide

  25. 5
    DEMO
    6 . 2

    View Slide

  26. 5
    LESSONS LEARNED
    How to use Parameterized Tests?
    Writing a custom ArgumentsProvider that loads data
    from a JSON le, …
    6 . 3

    View Slide

  27. 5
    FROM TESTS TO TEST
    TEMPLATES
    7 . 1

    View Slide

  28. 5
    DEMO
    7 . 2

    View Slide

  29. 5
    LESSONS LEARNED
    How to execute a test multiple times with di erent
    contexts?
    How to implement a TestInvocationContextProvider
    7 . 3

    View Slide

  30. 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

    View Slide

  31. 5
    SUMMARY
    8 . 1

    View Slide

  32. 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

    View Slide

  33. 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

    View Slide

  34. 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

    View Slide

  35. 5
    THANK YOU!
    Example code:
    https://github.com/marcphilipp/junit5-extensions-demo
    8 . 5

    View Slide