Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

JUnit 5

133 views

Published on

A short presentation on JUnit 5.

Sample code on Github at: https://github.com/sleberknight/junit5-presentation-code

Published in: Technology
  • Be the first to comment

  • Be the first to like this

JUnit 5

  1. 1. Scott Leberknight 8/2/2018 JUnit
  2. 2. “…the next generation of JUnit…”
  3. 3. “…for Java 8 and beyond…”
  4. 4. “…enabling many different styles of testing”
  5. 5. Key Takeaways…
  6. 6. Next gen test framework… New programming & extension model for developers Stable platform for tool providers Migration path from earlier versions
  7. 7. Platform + Jupiter + Vintage JUnit 5 =
  8. 8. JUnit Platform Launcher for test frameworks on JVM Defines TestEngine API Console Launcher Maven, Gradle plugins
  9. 9. JUnit Jupiter New programming model Standard assertions Extension model (replaces @Rules) TestEngine for running Jupiter tests
  10. 10. JUnit Vintage TestEngine to run JUnit 3 & 4 tests Eases migration to Jupiter…
  11. 11. Dependencies
  12. 12. Separate dependency groups… org.junit.platform org.junit.jupiter org.junit.vintage
  13. 13. junit-jupiter-api junit-jupiter-engine junit-jupiter-migrationsupport junit-vintage-engine …and more common dependencies…
  14. 14. Writing tests
  15. 15. import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class FirstJupiterTest { @Test void theAnswer() { assertEquals(42, 22 + 20); } }
  16. 16. Jupiter Basics No need to be public Annotation-based (like JUnit 4) Assertions & Assumptions
  17. 17. @Test @DisplayName("All Your Base Are Belong To Us™") void allYourBase() { assertAll( () -> assertTrue(true), () -> assertEquals("42", "4" + "2"), () -> assertTimeout(ofSeconds(1), () -> 42 * 42) ); } @Test @DisplayName("To ∞ & beyond...") void toInfinity() { Integer result = assertDoesNotThrow(() -> ThreadLocalRandom.current().nextInt(42)); assertTrue(result < 42); }
  18. 18. @Test
  19. 19. @Test indicates a test method Test methods cannot be private or static Test methods can declare parameters Jupiter supports meta-annotations
  20. 20. @Slf4j class FirstParameterTest { @BeforeEach void setUp(TestInfo info) { LOG.info("Executing test: {} tagged with {}", info.getDisplayName(), info.getTags()); } @Test @Tag("fast") void multiply() { assertEquals(42, 21 * 2); } } ( @Slf4j is a Lombok annotation that generates an SLF4J Logger )
  21. 21. Jupiter Annotations Reside in org.junit.jupiter.api Some common ones: @BeforeEach, @AfterEach @BeforeAll, @AfterAll @DisplayName @Tag @Disabled
  22. 22. Jupiter Annotations And some more exotic ones… @Nested @TestInstance @TestFactory @TestTemplate @ParameterizedTest @RepeatedTest
  23. 23. Meta-Annotations @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Tag("fast") @interface Fast {} @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Test @Fast public @interface FastTest {} @FastTest void lightning() { assertFalse(false); }
  24. 24. @TestInstance Controls lifecycle of test instances Per method is the default; creates new instance for every test Per class re-uses the same instance for every test
  25. 25. @TestInstance(TestInstance.Lifecycle.PER_CLASS) @Slf4j class PerClassTestLifecycleTest { private int sum = 0; @Test void add5() { sum += 5; } @Test void add10() { sum += 10; } @AfterEach void tearDown() { LOG.info("The current sum is: {}", sum); } }
  26. 26. @TestInstance In general, stick with the default PER_METHOD lifecycle… …and become aware of differences (pitfalls?) in behavior when using PER_CLASS
  27. 27. @Nested Allows arbitrary levels of nesting Group common sets of tests Cleanly separate setup/teardown code
  28. 28. @DisplayName("An ArrayList") class NestedExampleTest { private ArrayList<String> list; @Nested @DisplayName(“when new”) class WhenNew { @BeforeEach void setUp() { list = new ArrayList<>(); } @Test @DisplayName(“is empty”) void isEmpty() { ... } @Nested @DisplayName(“after adding an element”) class AfterAddElement { @BeforeEach void setUp() { list.add(“an item”); } @Test @DisplayName(“is no longer empty”) void isNotEmpty() { ... } } } }
  29. 29. Nested test output
  30. 30. @ParameterizedTest Execute tests multiple times with different arguments Requires a source of values Currently an experimental feature!
  31. 31. class ParametersTest { @ParameterizedTest @ValueSource(ints = {2, 4, 6, 8, 10}) void evens(int value) { assertEquals(0, value % 2, () -> String.format("Expected %d mod 2 == 0", value)); } @ParameterizedTest @MethodSource("supplyOdds") void odds(int value) { assertEquals(1, value % 2); } private static Stream<Integer> supplyOdds() { return Stream.of(1, 3, 5, 7, 9, 11); } }
  32. 32. Other value sources Enums CSV strings and files Using ArgumentsProvider API
  33. 33. Dependency Injection Constructors & methods can have parameters! DIY using ParameterResolver API Several built-in resolvers (TestInfo, RepetitionInfo, and TestReporterParameterResolver)
  34. 34. @RepeatedTest(value = 10) void repeatedTestWithInfo(RepetitionInfo repetitionInfo) { assertTrue(repetitionInfo.getCurrentRepetition() <= repetitionInfo.getTotalRepetitions()); assertEquals(10, repetitionInfo.getTotalRepetitions()); }
  35. 35. More… Disabling & conditional execution Tagging & filtering Repeated tests Assumptions Test templates & dynamic tests
  36. 36. A word on Assertions Jupiter provides the basics… …and several more useful ones But consider using 3rd party assertion frameworks like AssertJ, Hamcrest, or Google’s Truth (asssertAll, assertTimeout, assertThrows, etc.)
  37. 37. Extension Model
  38. 38. Extension API Replaces JUnit 4 Runner, @Rule, & @ClassRule Extension “marker” interface Lifecycle callbacks define extension points Constructor & method parameters
  39. 39. Registering extensions Declarative via @ExtendWith Programmatically (code + @RegisterExtension) Java’s ServiceLoader mechanism
  40. 40. @ExtendWith Apply to test classes & methods Register multiple extensions
  41. 41. @ExtendWith(TempDirectory.class) class FileExporterTest { private Path tempDirectory; @BeforeEach void setUp(@TempDirectory.Root Path tempDir) { this.tempDirectory = tempDir; } // ... }
  42. 42. class ReportGeneratorTest { @ExtendWith(TempDirectory.class) @Test void excelReport(@TempDirectory.Root Path tempDir) { // ... } @Test void clipboardReport() { ... } @ExtendWith(TempDirectory.class) @Test void pdfReport(@TempDirectory.Root Path tempDir) { // ... } }
  43. 43. @RegisterExtension Programmatically construct extensions Can register multiple extensions
  44. 44. class RandomThingsTest { @RegisterExtension final SoftAssertionsExtension softly = new SoftAssertionsExtension(); @Test void addition() { softly.assertThat(2 + 2).isEqualTo(4); // ... } @Test void substrings() { String str = "Quick brown fox jumped over the lazy dog"; softly.assertThat(str).contains("Quick"); softly.assertThat(str).contains(“brown"); softly.assertThat(str).contains(“lazy"); softly.assertThat(str).contains(“dog"); } }
  45. 45. Extension lifecycle
  46. 46. BeforeAllCallback BeforeEachCallback BeforeTestExecutionCallback AfterTestExecutionCallback AfterEachCallback AfterAllCallback TestExecutionExceptionHandlerTest happens here (*)
  47. 47. A simple extension…
  48. 48. @Slf4j public class SoftAssertionsExtension extends SoftAssertions implements AfterEachCallback { public SoftAssertionsExtension() { LOG.trace("Constructed new instance"); } public void afterEach(ExtensionContext context) { LOG.trace("Asserting all soft assertions”); assertAll(); } } SoftAssertions is an AssertJ class
  49. 49. More extension features Automatic registration via ServiceLoader Keeping state via ExtensionContext.Store ParameterResolver for resolving parameters Test instance post-processing Conditional test execution (disabled by default)
  50. 50. Migrating from JUnit 4…
  51. 51. No @Rules Without any rules, it’s pretty easy! …mostly changing imports …and some annotations, e.g. @Before to @BeforeEach
  52. 52. With @Rules With existing rules, it depends… Converting most rules is fairly easy Many frameworks already have JUnit 5 extensions (e.g. Dropwizard, Spring, Mockito, …)
  53. 53. Migration Support Must use @EnableRuleMigrationSupport Includes support for: ExternalResource ExpectedException Verifier (including ErrorCollector) (*) (*) this probably covers a lot of existing rules
  54. 54. APIs Support API evolution over time Mark public interface with @API status Uses @APIGuardian for status, e.g. STABLE, INTERNAL, EXPERIMENTAL
  55. 55. Wrap up New programming & extension model Allows migration over time and keeping JUnit 4 & 5 in same project Separates platform from test engines Recommend use 3rd-party assertion library
  56. 56. sample code available at: https://github.com/sleberknight/junit5-presentation-code
  57. 57. References JUnit 5 on GitHub https://github.com/junit-team/junit5 @API Guardian https://github.com/apiguardian-team/apiguardian JUnit 5 web site https://junit.org/junit5/ JUnit 5 User Guide https://junit.org/junit5/docs/current/user-guide/
  58. 58. Dom Pérignon https://www.flickr.com/photos/tromal/6989654843 https://creativecommons.org/licenses/by/2.0/ No changes made Photos & Images (All other images were purchased from iStock) Jupiter Beer https://www.flickr.com/photos/quinnanya/30680378305/ https://creativecommons.org/licenses/by-sa/2.0/ No changes made
  59. 59. My Info sleberknight at fortitudetec.com www.fortitudetec.com @sleberknight scott.leberknight at gmail

×