Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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());

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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);

Slide 35

Slide 35 text

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);

Slide 36

Slide 36 text

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));

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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"); }

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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()); }

Slide 52

Slide 52 text

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); }

Slide 53

Slide 53 text

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()); }

Slide 54

Slide 54 text

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(); }

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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