$30 off During Our Annual Pro Sale. View Details »

AssertJ: Hidden Gems of Java Developer Testing

Ted M. Young
PRO
June 22, 2023
25

AssertJ: Hidden Gems of Java Developer Testing

Ted M. Young
PRO

June 22, 2023
Tweet

Transcript

  1. Ted M. Young https://ted.dev
    AssertJ
    Hidden Gem of
    Testing
    Ted M. Young
    Java Trainer, Coach, & Live Coder
    [email protected]
    https://Ted.dev/about

    View Slide

  2. Ted M. Young https://ted.dev
    Tag #AssertJ
    & #KCDC2023
    @JitterTed
    sfba.social/@JitterTed

    View Slide

  3. Ted M. Young https://ted.dev
    AssertJ Poll
    Raise Your Hands If You're Using AssertJ Today

    View Slide

  4. Ted M. Young https://ted.dev
    AssertJ Poll: Raise Hands if using…
    •isEqualByComparingTo()
    •contains()
    • containsExactly()
    • containsExactlyInAnyOrderElementsOf()
    •extracting()
    •usingRecursiveComparison()
    • RecursiveComparisonConfiguration

    View Slide

  5. Ted M. Young https://ted.dev
    I Live for Autocomplete
    How did we code without it?

    View Slide

  6. Ted M. Young https://ted.dev
    Assertion Library Purpose
    More Readable Failure Results
    and
    More Readable Test Code

    View Slide

  7. Ted M. Young https://ted.dev
    I 💚 TDD

    View Slide

  8. Ted M. Young https://ted.dev
    Read More Than Write
    Readability of Test Code is Paramount

    View Slide

  9. Ted M. Young https://ted.dev
    AssertJ is HUGE
    I won't cover (can't cover) everything, because…

    View Slide

  10. Ted M. Young https://ted.dev
    So Many Ways to Assert!

    View Slide

  11. Ted M. Young https://ted.dev
    Many Ways to Assert
    Sometimes Too Many!

    View Slide

  12. Ted M. Young https://ted.dev
    So Many Ways to Assert!

    View Slide

  13. Ted M. Young https://ted.dev
    Our Agenda
    • Basics: comparing individual values
    • Improving output with Description
    • Collections and Streams
    • Exceptions
    • Raise level of abstraction
    • Condition
    • Custom assertion classes
    • Tools and other tips

    View Slide

  14. Ted M. Young https://ted.dev
    JUnit Assertions
    vs AssertJ
    Preaching to the choir?

    View Slide

  15. Ted M. Young https://ted.dev
    JUnit vs. AssertJ
    // JUnit
    assertTrue(inventoryProduct.isInStock());
    // AssertJ
    assertThat(inventoryProduct.isInStock()).isTrue();

    View Slide

  16. Ted M. Young https://ted.dev
    JUnit vs. AssertJ
    // JUnit
    assertEquals(inventoryProduct.quantityAvailable(), 10);
    // AssertJ
    assertThat(inventoryProduct.quantityAvailable()).isEqualTo(10);

    View Slide

  17. Ted M. Young https://ted.dev
    JUnit vs. AssertJ
    // JUnit
    assertEquals(inventoryProduct.quantityAvailable(), 10);
    assertEquals(10, inventoryProduct.quantityAvailable());
    // AssertJ
    assertThat(inventoryProduct.quantityAvailable()).isEqualTo(10);

    View Slide

  18. Ted M. Young https://ted.dev
    AssertJ: Wide Layout
    assertThat(actualResultFromCodeUnderTest).isEqualTo(expectedOutcome);

    View Slide

  19. Ted M. Young https://ted.dev
    Slow to scan
    assertThat(actualResultFromCodeUnderTest).isEqualTo(expectedOutcome);

    View Slide

  20. Ted M. Young https://ted.dev
    Horizontal scrolling is even slower
    assertThat(rod).as("not == ").isNotSameAs(roderick);
    assertThat(rod.getName()).as("rod.name is Rod").isEqualTo("Rod");
    assertThat(rod.getAge()).as("rod.age is 31").isEqualTo(31);
    assertThat(roderick.getName()).as("roderick.name is Roderick").isEqualTo("Roderick");
    assertThat(roderick.getAge()).as("roderick.age was inherited").isEqualTo(rod.getAge());

    View Slide

  21. Ted M. Young https://ted.dev
    Prefer Stacked Layout: from simple
    assertThat(actualResultFromCodeUnderTest)
    .isEqualTo(expectedOutcome);
    "Stacked"
    Layout

    View Slide

  22. Ted M. Young https://ted.dev
    …to lots of simple…
    assertThat(rod)
    .as("not == ")
    .isNotSameAs(roderick);
    assertThat(rod.getName())
    .as("rod.name is Rod")
    .isEqualTo("Rod");
    assertThat(rod.getAge())
    .as("rod.age is 31")
    .isEqualTo(31);
    assertThat(roderick.getName())
    .as("roderick.name is Roderick")
    .isEqualTo("Roderick");
    assertThat(roderick.getAge())
    "Stacked"
    Layout

    View Slide

  23. Ted M. Young https://ted.dev
    …to the complex
    assertThat(productInventory)
    .extracting(
    InventoryProduct::quantityAvailable,
    InventoryProduct::price)
    .usingRecursiveFieldByFieldElementComparator(forBigDecimalTuple)
    .containsExactly(
    tuple(10, BigDecimal.valueOf(24.95)),
    tuple(1, BigDecimal.valueOf(12.00)));
    "Stacked"
    Layout

    View Slide

  24. Ted M. Young https://ted.dev
    Some Code is Broken
    This is For Demonstration Purposes Only

    View Slide

  25. Ted M. Young https://ted.dev
    The Basics

    View Slide

  26. Ted M. Young https://ted.dev
    Asserting Booleans
    True? False? What does that MEAN?

    View Slide

  27. Ted M. Young https://ted.dev
    Boolean assertions stink
    org.opentest4j.AssertionFailedError:
    Expecting value to be true but was false
    Expected :true
    Actual :false

    View Slide

  28. Ted M. Young https://ted.dev
    Use Description via as()
    BooleanTest

    View Slide

  29. Ted M. Young https://ted.dev
    Enum: isEqualByComparingTo
    EnumTest

    View Slide

  30. Ted M. Young https://ted.dev
    BigDecimal Weirdness
    BigDecimalTest

    View Slide

  31. Ted M. Young https://ted.dev
    Magic of extracting()
    // SimpleBook("title", "author", publishedYear)
    SimpleBook book = new SimpleBook("Mastering AssertJ", "JitterTed", 2022);
    assertThat(book.publishedYear())
    .isEqualTo(2023);
    assertThat(book)
    .extracting("publishedYear")
    .isEqualTo(2023);
    AssertionFailedError:
    expected: 2023
    but was: 2022
    AssertionFailedError:
    [Extracted: publishedYear]
    expected: 2023
    but was: 2022

    View Slide

  32. Ted M. Young https://ted.dev
    Refactor-friendly extracting()
    // SimpleBook("title", "author", publishedYear)
    SimpleBook book = new SimpleBook("Mastering AssertJ", "JitterTed", 2022);
    assertThat(book)
    .extracting(SimpleBook::publishedYear)
    .isEqualTo(2023);
    AssertionFailedError:
    expected: 2023
    but was: 2022

    View Slide

  33. Ted M. Young https://ted.dev
    Not Compile-time Type-safe
    // SimpleBook("title", "author", publishedYear)
    SimpleBook book = new SimpleBook("Mastering AssertJ", "JitterTed", 2022);
    assertThat(book)
    .extracting(SimpleBook::publishedYear)
    .isEqualTo("2023");
    AssertionFailedError:
    expected: "2022"
    but was: 2022

    View Slide

  34. Ted M. Young https://ted.dev
    Multiple extracting()
    // SimpleBook("title", "author", publishedYear)
    SimpleBook book = new SimpleBook("Mastering AssertJ", "JitterTed", 2023);
    assertThat(book)
    .extracting(SimpleBook::author, SimpleBook::publishedYear)
    .containsExactly("JitterTed", 2023);

    View Slide

  35. Ted M. Young https://ted.dev
    How about returns()?
    // SimpleBook("title", "author", publishedYear)
    SimpleBook book = new SimpleBook("Mastering AssertJ", "JitterTed", 2023);
    assertThat(book)
    .returns("2023", SimpleBook::publishedYear);

    View Slide

  36. Ted M. Young https://ted.dev
    Syntactic sugar from() doesn't help
    // SimpleBook("title", "author", publishedYear)
    SimpleBook book = new SimpleBook("Mastering AssertJ", "JitterTed", 2023);
    assertThat(book)
    .returns("2023", from(SimpleBook::publishedYear));

    View Slide

  37. Ted M. Young https://ted.dev
    COLLECTIONS
    Where AssertJ shows its intention-revealing power

    View Slide

  38. Ted M. Young https://ted.dev
    Lists, Sets, Streams
    and other Iterables

    View Slide

  39. Ted M. Young https://ted.dev
    Iterable Attributes:
    empty()/notEmpty()

    View Slide

  40. Ted M. Young https://ted.dev
    Iterable Attributes:
    hasSize()

    View Slide

  41. Ted M. Young https://ted.dev
    Iterable Contents:
    contains()

    View Slide

  42. Ted M. Young https://ted.dev
    ContainsExactly()
    @Test
    void parseWithNoQuotesParsesCorrectly() throws Exception {
    String row = "123,456,789";
    List values = Book.parseQuotedCsv(row);
    assertThat(values)
    .containsExactly("123", "456", "789");
    }

    View Slide

  43. Ted M. Young https://ted.dev
    extracting() & tuple()
    ExtractingTupleRecursiveTest

    View Slide

  44. Ted M. Young https://ted.dev
    filteredOn()
    BeforeAfterSimpleBookCollectionTest.filtered

    View Slide

  45. Ted M. Young https://ted.dev
    Predicate & Condition
    BookCatalogTest

    View Slide

  46. Ted M. Young https://ted.dev
    Predicate with Description
    assertThat(catalog.allBooks())
    .allMatch(book -> book.yearPublished() >= 1996, "published since 1996")
    Expecting all elements of:
    [Book {title='Design Patterns', authors=…yearPublished=1994},
    Book {title='Refactoring, 2nd Edition', authors=…yearPublished=2018}]
    to match 'published since 1996' predicate but this element did not:
    Book {title='Design Patterns', authors=…yearPublished=1994}
    Predicate description

    View Slide

  47. Ted M. Young https://ted.dev
    Predicate vs. Condition
    Expecting all elements of:
    [Book {title='Design Patterns', authors=…yearPublished=1994},
    Book {title='Refactoring, 2nd Edition', authors=…yearPublished=2018}]
    to match 'published since 1996' predicate but this element did not:
    Book {title='Design Patterns', authors=…yearPublished=1994}
    Expecting elements:
    [Book {title='Design Patterns', authors=…yearPublished=1994}]
    of
    [Book {title='Design Patterns', authors=…yearPublished=1994},
    Book {title='Refactoring, 2nd Edition', authors=…yearPublished=2018}]
    to be published in the 21st century
    Predicate
    Condition

    View Slide

  48. Ted M. Young https://ted.dev
    Maps
    containsEntry, containsKeys, containsValues

    View Slide

  49. Ted M. Young https://ted.dev
    Exception Checking
    OrderedDeckTest

    View Slide

  50. Ted M. Young https://ted.dev
    Blackjack: Face Up/Down Cards

    View Slide

  51. Ted M. Young https://ted.dev
    Blackjack Game Standard Assert
    @Test void playerStandsDealerCardsFaceUp() {
    Deck deck = StubDeckBuilder.playerCountOf(1)
    .addPlayerWithRanks(Rank.TEN, Rank.JACK)
    .buildWithDealerDoesNotDrawCards();
    Game game = GameFactory.createOnePlayerGamePlaceBetsInitialDeal(deck);
    game.playerStands();
    assertThat(game.dealerHand().cards())
    .allMatch(card -> !card.isFaceDown());
    }

    View Slide

  52. Ted M. Young https://ted.dev
    With Predicate
    @Test void playerStandsDealerCardsFaceUp() {
    Deck deck = StubDeckBuilder.playerCountOf(1)
    .addPlayerWithRanks(Rank.TEN, Rank.JACK)
    .buildWithDealerDoesNotDrawCards();
    Game game = GameFactory.createOnePlayerGamePlaceBetsInitialDeal(deck);
    game.playerStands();
    Predicate faceUpCardPredicate = card -> !card.isFaceDown();
    assertThat(game.dealerHand().cards())
    .allMatch(faceUpCardPredicate);
    }

    View Slide

  53. Ted M. Young https://ted.dev
    Encapsulate Assertion
    @Test void playerStandsDealerAllCardsFaceUp_predicate() {
    Deck deck = StubDeckBuilder.playerCountOf(1)
    .addPlayerWithRanks(Rank.TEN, Rank.JACK)
    .buildWithDealerDoesNotDrawCards();
    Game game = GameFactory.createOnePlayerGamePlaceBetsInitialDeal(deck);
    game.playerStands();
    assertAllDealerCardsFaceUp(game);
    }
    static void assertAllDealerCardsFaceUp(Game game) {
    assertThat(game.dealerHand().cards())
    .allMatch(card -> !card.isFaceDown());
    }

    View Slide

  54. Ted M. Young https://ted.dev
    Blackjack Game Custom Asserts
    @Test void playerStandsDealerCardsFaceUp() {
    Deck deck = StubDeckBuilder.playerCountOf(1)
    .addPlayerWithRanks(Rank.TEN, Rank.JACK)
    .buildWithDealerDoesNotDrawCards();
    Game game = GameFactory.createOnePlayerGamePlaceBetsInitialDeal(deck);
    game.playerStands();
    assertThat(game)
    .dealerHand()
    .allCardsFaceUp();
    }

    View Slide

  55. Ted M. Young https://ted.dev
    Let's Modernize
    Joda Money Tests
    Moving from JUnit to AssertJ

    View Slide

  56. Ted M. Young https://ted.dev
    Writing MoneyAssert
    Custom Assertions for Joda Money

    View Slide

  57. Ted M. Young https://ted.dev
    Soft Asserts
    AllOrSoftlyTest

    View Slide

  58. Ted M. Young https://ted.dev
    Plugin helps to convert to AssertJ
    Concise AssertJ
    Optimizing Nitpicker
    (Cajon)

    View Slide

  59. Ted M. Young https://ted.dev
    Also suggests improvements

    View Slide

  60. Ted M. Young https://ted.dev
    AssertJ
    (So) Many Ways to
    Make Test Code &
    Results More
    Readable
    Ted M. Young
    Java Trainer, Coach, & Live Coder
    Get in touch: [email protected]
    Twitter: @JitterTed
    Twitch: https://JitterTed.LIVE
    YouTube: https://JitterTed.TV
    Web: https://ted.dev

    View Slide

  61. Ted M. Young https://ted.dev
    Thank You
    Have a Great Day!
    Ted M. Young
    Java Trainer, Coach, & Live Coder
    Get in touch: [email protected]
    Twitter: @JitterTed
    Twitch: https://JitterTed.LIVE
    YouTube: https://JitterTed.TV
    Web: https://ted.dev

    View Slide