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

Building Robust Software, Episode 3

Building Robust Software, Episode 3

Presented at https://www.meetkt.org on 17 December 2021

This episode explains how the design of the production code can affect the maintenance of tests. We also touch upon different kinds of testing tools and methodologies suited for various software development and maintenance scenarios.

Ragunath Jawahar

December 17, 2021
Tweet

More Decks by Ragunath Jawahar

Other Decks in Programming

Transcript

  1. Episode 1: Small ideas, big impact • Robustness principle &

    a tweaked version of it • Algebraic data types • Total & partial functions • Minimising defensive programming • Using the type system to short-circuit failures (a.k.a failing fast) • Avoiding primitive obsession
  2. Episode 2: Exceptions, side-effects and boundaries • Exception anti-patterns •

    Handling • Throwing • Tester-Doer pattern • Try-Parse Pattern • Converting exceptions to values • Introduction to values as boundaries • Architectures that use values as boundaries
  3. Scenario An event management startup has hired you and needs

    your help to build a feature that sends reminder emails to potential attendees for an upcoming event.
  4. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  5. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  6. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  7. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  8. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  9. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  10. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  11. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  12. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  13. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  14. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  15. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  16. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  17. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  18. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  19. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  20. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  21. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  22. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  23. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  24. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  25. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  26. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  27. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  28. Scenario Your company is losing money 💸 💸 💸 due

    to bug fi xes in production, so your team has decided to write tests. It's your responsibility to bring your modules under test.
  29. No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone RSVP’d

    “Maybe” A mix of “Yes”, “No”, and “Maybe”
  30. No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone RSVP’d

    “Maybe” A mix of “Yes”, “No”, and “Maybe”
  31. No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone RSVP’d

    “Maybe” A mix of “Yes”, “No”, and “Maybe”
  32. No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone RSVP’d

    “Maybe” A mix of “Yes”, “No”, and “Maybe”
  33. No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone RSVP’d

    “Maybe” A mix of “Yes”, “No”, and “Maybe”
  34. No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone RSVP’d

    “Maybe” A mix of “Yes”, “No”, and “Maybe”
  35. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  36. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  37. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  38. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  39. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  40. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  41. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  42. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  43. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  44. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  45. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  46. @Test fun `don't send emails when there are no registrants`()

    { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) whenever(dao.getRegistrants(event.id)).thenReturn(emptyList()) val event = Event( "Building Robust Software III", "Event is on 16th December, please attend." ) // when eventManager.sendReminder(event) // then verifyNoInteractions(mailer) }
  47. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  48. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  49. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  50. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  51. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  52. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  53. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  54. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  55. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  56. @Test fun `email only those who RSVP'd yes and maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  57. ✅ No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone

    RSVP’d “Maybe” ✅ A mix of “Yes”, “No” and “Maybe”
  58. Sorta real thing • Stubs • Mocks • Fakes •

    Dummies • Spies https://martinfowler.com/articles/mocksArentStubs.html
  59. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  60. Current EventManager design • Encapsulates business logic • Who should

    be noti fi ed? • Coordination logic • Who is talking to who?
  61. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES || it.rsvp = = MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } a.EventManager.kt
  62. Current EventManager design • Encapsulates business logic • Who should

    be noti fi ed? • Coordination logic • Who is talking to who?
  63. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .potentialAttendees() potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } class EventManager( private val dao: EventDao, private val mailer: Mailer ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES | | it.rsvp == MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } }
  64. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .potentialAttendees() potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } class EventManager( private val dao: EventDao, private val mailer: Mailer ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES | | it.rsvp == MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } }
  65. class EventManager( private val dao: EventDao, private val mailer: Mailer

    ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .potentialAttendees() potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } } class EventManager( private val dao: EventDao, private val mailer: Mailer ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .filter { it.rsvp == YES | | it.rsvp == MAYBE } potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } }
  66. b.EventManager.kt class EventManager( private val dao: EventDao, private val mailer:

    Mailer ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .potentialAttendees() potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } }
  67. b.EventManager.kt class EventManager( private val dao: EventDao, private val mailer:

    Mailer ) { fun sendReminder(event: Event) { val registrants = dao.getRegistrants(event.id) val potentialAttendees = registrants .potentialAttendees() potentialAttendees.onEach { attendee -> mailer.mail(attendee.emailAddress, event.reminder) } } }
  68. No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone RSVP’d

    “Maybe” A mix of “Yes”, “No”, and “Maybe”
  69. @Test fun `no potential attendees for an empty list`() {

    // given val noRegistrants = emptyList<User>() // when val potentialAttendees = noRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .isEmpty() }
  70. @Test fun `no potential attendees for an empty list`() {

    // given val noRegistrants = emptyList<User>() // when val potentialAttendees = noRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .isEmpty() }
  71. @Test fun `no potential attendees for an empty list`() {

    // given val noRegistrants = emptyList<User>() // when val potentialAttendees = noRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .isEmpty() }
  72. @Test fun `no potential attendees for an empty list`() {

    // given val noRegistrants = emptyList<User>() // when val potentialAttendees = noRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .isEmpty() }
  73. @Test fun `no potential attendees for an empty list`() {

    // given val noRegistrants = emptyList<User>() // when val potentialAttendees = noRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .isEmpty() }
  74. @Test fun `no potential attendees for an empty list`() {

    // given val noRegistrants = emptyList<User>() // when val potentialAttendees = noRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .isEmpty() }
  75. @Test fun `no potential attendees for an empty list`() {

    // given val noRegistrants = emptyList<User>() // when val potentialAttendees = noRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .isEmpty() }
  76. @Test fun `potential attendees contains only registrants those who RSVP'd

    yes or maybe`() { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) }
  77. @Test fun `potential attendees contains only registrants those who RSVP'd

    yes or maybe`() { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) }
  78. @Test fun `potential attendees contains only registrants those who RSVP'd

    yes or maybe`() { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) }
  79. @Test fun `potential attendees contains only registrants those who RSVP'd

    yes or maybe`() { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) }
  80. @Test fun `potential attendees contains only registrants those who RSVP'd

    yes or maybe`() { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) }
  81. @Test fun `potential attendees contains only registrants those who RSVP'd

    yes or maybe`() { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) }
  82. @Test fun `potential attendees contains only registrants those who RSVP'd

    yes or maybe`() { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) }
  83. ✅ No registrations Everyone RSVP’d “Yes” Everyone RSVP’d “No” Everyone

    RSVP’d “Maybe” ✅ A mix of “Yes”, “No” and “Maybe”
  84. I O

  85. Newer EventManager design • Does not encapsulate business logic •

    Can use micro unit tests to test various cases • Value-in, value-out results in decoupled design • No dependencies for business logic
  86. Testing the coordination logic @Test fun `query and email folks

    who RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  87. Testing the coordination logic @Test fun `query and email folks

    who RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  88. Testing the coordination logic @Test fun `query and email folks

    who RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  89. Testing the coordination logic @Test fun `query and email folks

    who RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  90. Testing the coordination logic @Test fun `query and email folks

    who RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  91. Testing the coordination logic @Test fun `query and email folks

    who RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  92. Implementation A vs. B @Test fun `RSVP'd yes or maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) } @Test fun `RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  93. Implementation A vs. B @Test fun `RSVP'd yes or maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) } @Test fun `RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  94. Implementation A vs. B @Test fun `RSVP'd yes or maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) } @Test fun `RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  95. Implementation A vs. B @Test fun `RSVP'd yes or maybe`()

    { // given val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) // when val potentialAttendees = allRegistrants.potentialAttendees() // then assertThat(potentialAttendees) .containsExactly( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.MAYBE) ) } @Test fun `RSVP'd yes and maybe to the event`() { // given val dao = mock<EventDao>() val mailer = mock<Mailer>() val eventManager = EventManager(dao, mailer) val event = Event( 1, "Building Robust Software III", "Event is on 16th December, please attend." ) val allRegistrants = listOf( User("[email protected]", Rsvp.YES), User("[email protected]", Rsvp.NO), User("[email protected]", Rsvp.MAYBE) ) whenever(dao.getRegistrants(event.id)).thenReturn(allRegistrants) // when eventManager.sendReminder(event) // then verify(mailer).mail("[email protected]", event.reminder) verify(mailer).mail("[email protected]", event.reminder) verifyNoMoreInteractions(mailer) }
  96. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  97. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  98. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  99. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  100. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  101. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  102. Implementation A vs. B import com.google.common.truth.Truth.assertThat import org.junit.jupiter.api.Test import xyz.ragunath.events.Rsvp

    import xyz.ragunath.events.User import org.junit.jupiter.api.Test import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import xyz.ragunath.events.Event import xyz.ragunath.events.EventDao import xyz.ragunath.events.Mailer import xyz.ragunath.events.Rsvp import xyz.ragunath.events.User
  103. Implementation A vs. B import com.google.common.truth.Truth.assertThat import org.junit.jupiter.api.Test import xyz.ragunath.events.Rsvp

    import xyz.ragunath.events.User import org.junit.jupiter.api.Test import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import xyz.ragunath.events.Event import xyz.ragunath.events.EventDao import xyz.ragunath.events.Mailer import xyz.ragunath.events.Rsvp import xyz.ragunath.events.User
  104. Implementation A vs. B import com.google.common.truth.Truth.assertThat import org.junit.jupiter.api.Test import xyz.ragunath.events.Rsvp

    import xyz.ragunath.events.User import org.junit.jupiter.api.Test import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import xyz.ragunath.events.Event import xyz.ragunath.events.EventDao import xyz.ragunath.events.Mailer import xyz.ragunath.events.Rsvp import xyz.ragunath.events.User
  105. Implementation A vs. B import com.google.common.truth.Truth.assertThat import org.junit.jupiter.api.Test import xyz.ragunath.events.Rsvp

    import xyz.ragunath.events.User import org.junit.jupiter.api.Test import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import xyz.ragunath.events.Event import xyz.ragunath.events.EventDao import xyz.ragunath.events.Mailer import xyz.ragunath.events.Rsvp import xyz.ragunath.events.User
  106. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  107. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  108. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  109. Implementation A vs. B import com.google.common.truth.Truth.assertThat import org.junit.jupiter.api.Test import xyz.ragunath.events.Rsvp

    import xyz.ragunath.events.User import org.junit.jupiter.api.Test import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import xyz.ragunath.events.Event import xyz.ragunath.events.EventDao import xyz.ragunath.events.Mailer import xyz.ragunath.events.Rsvp import xyz.ragunath.events.User
  110. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  111. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  112. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  113. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  114. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  115. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  116. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  117. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  118. Implementation A vs. B • 5 integration tests • +

    Dependencies • ↑ Coupling • Wider change propagation • Harder to maintain • More assertions • 5 micro-unit tests • 1 integration test • - Dependencies • ↓ Coupling • Limited change propagation • Easier to maintain • Fewer assertions 💯
  119. What did we learn? • Behaviour testing • Value testing

    • Micro-unit tests • Complementary tests • Integration tests
  120. Scenario You inherit a codebase that does not have any

    tests, the previous developers have left the organisation and you are expected to enhance an existing feature.
  121. package com.gildedrose class GildedRose(var items: Array<Item>) { fun updateQuality() {

    for (i in items.indices) { if (items[i].name != "Aged Brie" && items[i].name ! = "Backstage passes to a TAFKAL80ETC concert") { if (items[i].quality > 0) { if (items[i].name != "Sulfuras, Hand of Ragnaros") { items[i].quality = items[i].quality - 1 } } } else { if (items[i].quality < 50) { items[i].quality = items[i].quality + 1 if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") { if (items[i].sellIn < 11) { if (items[i].quality < 50) { items[i].quality = items[i].quality + 1 } } if (items[i].sellIn < 6) { if (items[i].quality < 50) { items[i].quality = items[i].quality + 1 } } } } } if (items[i].name != "Sulfuras, Hand of Ragnaros") { items[i].sellIn = items[i].sellIn - 1 } if (items[i].sellIn < 0) { if (items[i].name != "Aged Brie") { if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") { if (items[i].quality > 0) { if (items[i].name != "Sulfuras, Hand of Ragnaros") { items[i].quality = items[i].quality - 1 } } } else { items[i].quality = items[i].quality - items[i].quality } } else { if (items[i].quality < 50) { items[i].quality = items[i].quality + 1 } } } } } }
  122. Writing tests for code you wrote vs. Writing tests for

    someone else’s code (characterisation tests)
  123. @Test fun `aged brie`() { // given val itemName =

    "Aged Brie" val sellIns = arrayOf(-1, 0, 1, 5, 6, 7, 10, 11, 12) val qualities = arrayOf(-1, 0, 1, 49, 50, 51) // when val function = updateQualityFunction(itemName) // (Int, Int) - > String // then CombinationApprovals.verifyAllCombinations(function, sellIns, qualities) }
  124. @Test fun `aged brie`() { // given val itemName =

    "Aged Brie" val sellIns = arrayOf(-1, 0, 1, 5, 6, 7, 10, 11, 12) val qualities = arrayOf(-1, 0, 1, 49, 50, 51) // when val function = updateQualityFunction(itemName) // (Int, Int) - > String // then CombinationApprovals.verifyAllCombinations(function, sellIns, qualities) }
  125. @Test fun `aged brie`() { // given val itemName =

    "Aged Brie" val sellIns = arrayOf(-1, 0, 1, 5, 6, 7, 10, 11, 12) val qualities = arrayOf(-1, 0, 1, 49, 50, 51) // when val function = updateQualityFunction(itemName) // (Int, Int) - > String // then CombinationApprovals.verifyAllCombinations(function, sellIns, qualities) }
  126. [-1, -1] => [Aged Brie, -2, 1] [-1, 0] =>

    [Aged Brie, -2, 2] [-1, 1] => [Aged Brie, -2, 3] [-1, 49] => [Aged Brie, -2, 50] [-1, 50] => [Aged Brie, -2, 50] [-1, 51] => [Aged Brie, -2, 51] [0, -1] => [Aged Brie, -1, 1] [0, 0] => [Aged Brie, -1, 2] [0, 1] => [Aged Brie, -1, 3] [0, 49] => [Aged Brie, -1, 50] [0, 50] => [Aged Brie, -1, 50] [0, 51] => [Aged Brie, -1, 51] [1, -1] => [Aged Brie, 0, 0] [1, 0] => [Aged Brie, 0, 1] [1, 1] => [Aged Brie, 0, 2] [1, 49] => [Aged Brie, 0, 50] [1, 50] => [Aged Brie, 0, 50] [1, 51] => [Aged Brie, 0, 51] [5, -1] => [Aged Brie, 4, 0] [5, 0] => [Aged Brie, 4, 1] [5, 1] => [Aged Brie, 4, 2] [5, 49] => [Aged Brie, 4, 50] [5, 50] => [Aged Brie, 4, 50] [5, 51] => [Aged Brie, 4, 51] [6, -1] => [Aged Brie, 5, 0] [6, 0] => [Aged Brie, 5, 1] [6, 1] => [Aged Brie, 5, 2] [6, 49] => [Aged Brie, 5, 50] [6, 50] => [Aged Brie, 5, 50] [6, 51] => [Aged Brie, 5, 51] [7, -1] => [Aged Brie, 6, 0] [7, 0] => [Aged Brie, 6, 1] [7, 1] => [Aged Brie, 6, 2] [7, 49] => [Aged Brie, 6, 50] [7, 50] => [Aged Brie, 6, 50] [7, 51] => [Aged Brie, 6, 51] [10, -1] => [Aged Brie, 9, 0] [10, 0] => [Aged Brie, 9, 1] [10, 1] => [Aged Brie, 9, 2] [10, 49] => [Aged Brie, 9, 50] [10, 50] => [Aged Brie, 9, 50] [10, 51] => [Aged Brie, 9, 51] [11, -1] => [Aged Brie, 10, 0] [11, 0] => [Aged Brie, 10, 1] [11, 1] => [Aged Brie, 10, 2] [11, 49] => [Aged Brie, 10, 50] [11, 50] => [Aged Brie, 10, 50] [11, 51] => [Aged Brie, 10, 51] [12, -1] => [Aged Brie, 11, 0] [12, 0] => [Aged Brie, 11, 1] [12, 1] => [Aged Brie, 11, 2] [12, 49] => [Aged Brie, 11, 50] [12, 50] => [Aged Brie, 11, 50] [12, 51] => [Aged Brie, 11, 51]
  127. Approval Tests • Snapshot testing, golden master testing, etc., •

    Helps bring large portions of code under tests quickly • Can work on di ff erent kinds of data
  128. Workflows • TDD • https://www.coderetreat.org/ • TCR • https://medium.com/@kentbeck_7670/test-commit-revert-870bbd756864 •

    https://github.com/LarsEckart/tcr-extension • https://github.com/ragunathjawahar/GildedRose-Refactoring-Kata/tree/rj/ attempt-04