Re-architecture

 Re-architecture

A8ee6ea52b89dfa1388b592a260c60a6?s=128

Ragunath Jawahar

February 14, 2020
Tweet

Transcript

  1. 2.

    ? • Started out as a Minimum Viable Product •

    Workarounds and fighting the framework • Overcome limitations • Accidental complexity (RxJava) • Open-Source (funded by a non-profit) • Painful tests (1:8)
  2. 4.
  3. 5.
  4. 6.
  5. 7.
  6. 9.

    System Boundaries • Network • File System / Database /

    Persistence • Hardware • UI • 3rd Party Integrations
  7. 10.
  8. 11.
  9. 12.
  10. 13.
  11. 19.

    + -

  12. 20.
  13. 25.

    + -

  14. 29.
  15. 30.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  16. 31.

    Different package Setup Tear down Test 1 Test 2 Test

    3 Test 4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  17. 32.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  18. 33.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  19. 34.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  20. 35.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  21. 36.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  22. 37.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  23. 38.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  24. 39.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  25. 40.

    Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt Setup Tear down Test 1 Test 2 Test 3 Test 4 LoginControllerTest.kt
  26. 41.
  27. 42.
  28. 43.

    Pros • Provides a fallback if things go wrong •

    Little refactoring experience • Allows complementing asymmetric/low-confidence tests with manual testing • “Works” even if you don’t have enough tests
  29. 44.

    Cons • All or nothing • Hard to accommodate incoming

    feature requests • Duplicate tests and features • Reviewing pull requests can be difficult • Easy to miss functionality that is not covered by tests • Cleaning up old code after feature stabilisation
  30. 47.
  31. 50.

    + -

  32. 51.

    + -

  33. 52.

    + -

  34. 53.

    + -

  35. 54.

    + -

  36. 55.

    + -

  37. 56.

    + -

  38. 57.

    + -

  39. 58.
  40. 59.

    Tear down Test 1 Test 2 Test 3 Test 4

    LoginControllerTest.kt Setup
  41. 60.

    Tear down Test 1 Test 2 Test 3 Test 4

    LoginControllerTest.kt Setup
  42. 61.

    Tear down Test 1 Test 2 Test 3 Test 4

    LoginControllerTest.kt Setup
  43. 62.

    Tear down Test 1 Test 2 Test 3 Test 4

    LoginControllerTest.kt Setup
  44. 64.
  45. 65.
  46. 66.
  47. 67.
  48. 69.

    @Test !// TODO: Migrate to Mobius fun `when save is

    clicked then user input should be validated`() { whenever(patientRepository.ongoingEntry()).thenReturn(Single.just(OngoingNewPatientEntry())) with(uiEvents) { onNext(FullNameChanged("")) onNext(PhoneNumberChanged("")) onNext(DateOfBirthChanged("")) onNext(AgeChanged("")) onNext(GenderChanged(None)) onNext(ColonyOrVillageChanged("")) onNext(DistrictChanged("")) onNext(StateChanged("")) onNext(SaveClicked) } with(uiEvents) { onNext(DateOfBirthChanged("33/33/3333")) onNext(SaveClicked) } with(uiEvents) { onNext(AgeChanged(" ")) onNext(DateOfBirthChanged("")) onNext(SaveClicked) } with(uiEvents) { onNext(DateOfBirthChanged("16/07/2018")) onNext(SaveClicked) } with(uiEvents) { onNext(PhoneNumberChanged("1234")) onNext(SaveClicked) } with(uiEvents) { onNext(PhoneNumberChanged("1234567890987654")) onNext(SaveClicked) } verify(ui, atLeastOnce()).showEmptyFullNameError(true) verify(ui, atLeastOnce()).showEmptyDateOfBirthAndAgeError(true) verify(ui, atLeastOnce()).showInvalidDateOfBirthError(true) verify(ui, atLeastOnce()).showMissingGenderError(true) verify(ui, atLeastOnce()).showEmptyColonyOrVillageError(true) verify(ui, atLeastOnce()).showEmptyDistrictError(true) verify(ui, atLeastOnce()).showEmptyStateError(true) verify(ui, atLeastOnce()).showLengthTooShortPhoneNumberError(true) verify(ui, atLeastOnce()).showLengthTooLongPhoneNumberError(true) }
  49. 70.
  50. 71.

    @Test !// TODO: Migrate to Mobius fun `when save is

    clicked then user input should be validated`() { whenever(patientRepository.ongoingEntry()).thenReturn(Single.just(OngoingNewPatientEntry())) with(uiEvents) { onNext(FullNameChanged("")) onNext(PhoneNumberChanged("")) onNext(DateOfBirthChanged("")) onNext(AgeChanged("")) onNext(GenderChanged(None)) onNext(ColonyOrVillageChanged("")) onNext(DistrictChanged("")) onNext(StateChanged("")) onNext(SaveClicked) } with(uiEvents) { onNext(DateOfBirthChanged("33/33/3333")) onNext(SaveClicked) } with(uiEvents) { onNext(AgeChanged(" ")) onNext(DateOfBirthChanged("")) onNext(SaveClicked) } with(uiEvents) { onNext(DateOfBirthChanged("16/07/2018")) onNext(SaveClicked) } with(uiEvents) { onNext(PhoneNumberChanged("1234")) onNext(SaveClicked) } with(uiEvents) { onNext(PhoneNumberChanged("1234567890987654")) onNext(SaveClicked) } verify(ui, atLeastOnce()).showEmptyFullNameError(true) verify(ui, atLeastOnce()).showEmptyDateOfBirthAndAgeError(true) verify(ui, atLeastOnce()).showInvalidDateOfBirthError(true) verify(ui, atLeastOnce()).showMissingGenderError(true) verify(ui, atLeastOnce()).showEmptyColonyOrVillageError(true) verify(ui, atLeastOnce()).showEmptyDistrictError(true) verify(ui, atLeastOnce()).showEmptyStateError(true) verify(ui, atLeastOnce()).showLengthTooShortPhoneNumberError(true) verify(ui, atLeastOnce()).showLengthTooLongPhoneNumberError(true) }
  51. 72.

    @Test !// TODO: Migrate to Mobius fun `when save is

    clicked then user input should be validated`() { whenever(patientRepository.ongoingEntry()).thenReturn(Single.just(OngoingNewPatientEntry())) with(uiEvents) { onNext(FullNameChanged("")) onNext(PhoneNumberChanged("")) onNext(DateOfBirthChanged("")) onNext(AgeChanged("")) onNext(GenderChanged(None)) onNext(ColonyOrVillageChanged("")) onNext(DistrictChanged("")) onNext(StateChanged("")) onNext(SaveClicked) } with(uiEvents) { onNext(DateOfBirthChanged("33/33/3333")) onNext(SaveClicked) } with(uiEvents) { onNext(AgeChanged(" ")) onNext(DateOfBirthChanged("")) onNext(SaveClicked) } with(uiEvents) { onNext(DateOfBirthChanged("16/07/2018")) onNext(SaveClicked) } with(uiEvents) { onNext(PhoneNumberChanged("1234")) onNext(SaveClicked) } with(uiEvents) { onNext(PhoneNumberChanged("1234567890987654")) onNext(SaveClicked) } verify(ui, atLeastOnce()).showEmptyFullNameError(true) verify(ui, atLeastOnce()).showEmptyDateOfBirthAndAgeError(true) verify(ui, atLeastOnce()).showInvalidDateOfBirthError(true) verify(ui, atLeastOnce()).showMissingGenderError(true) verify(ui, atLeastOnce()).showEmptyColonyOrVillageError(true) verify(ui, atLeastOnce()).showEmptyDistrictError(true) verify(ui, atLeastOnce()).showEmptyStateError(true) verify(ui, atLeastOnce()).showLengthTooShortPhoneNumberError(true) verify(ui, atLeastOnce()).showLengthTooLongPhoneNumberError(true) }
  52. 73.

    @Test !// TODO: Migrate to Mobius fun `when save is

    clicked then user input should be validated`() { whenever(patientRepository.ongoingEntry()).thenReturn(Single.just(OngoingNewPatientEntry())) with(uiEvents) { onNext(FullNameChanged("")) onNext(PhoneNumberChanged("")) onNext(DateOfBirthChanged("")) onNext(AgeChanged("")) onNext(GenderChanged(None)) onNext(ColonyOrVillageChanged("")) onNext(DistrictChanged("")) onNext(StateChanged("")) onNext(SaveClicked) } with(uiEvents) { onNext(DateOfBirthChanged("33/33/3333")) onNext(SaveClicked) } with(uiEvents) { onNext(AgeChanged(" ")) onNext(DateOfBirthChanged("")) onNext(SaveClicked) } with(uiEvents) { onNext(DateOfBirthChanged("16/07/2018")) onNext(SaveClicked) } with(uiEvents) { onNext(PhoneNumberChanged("1234")) onNext(SaveClicked) } with(uiEvents) { onNext(PhoneNumberChanged("1234567890987654")) onNext(SaveClicked) } }
  53. 74.
  54. 75.
  55. 76.
  56. 77.
  57. 78.
  58. 79.

    Move functionality to the new architecture and the test would

    fail indicating a successful port. Next, remove the functionality from the new architecture, now the current test & all other tests should pass.
  59. 80.
  60. 81.
  61. 82.
  62. 83.
  63. 85.
  64. 86.

    Pros • Pretty low risk, micro changes to the system

    • System is green and ready to ship all the time • Easy for the reviewer to understand system evolution • Fairly easy to accommodate incoming feature requests • Untested code attracts attention • No duplication
  65. 87.

    Cons • Requires familiarity around testing and refactoring • Requires

    fast feedback cycles • Requires experimentation • Has the highest pay-off when similar patterns are replicated • May inherit some design decisions from the older architecture