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

Re-architecture

 Re-architecture

A8ee6ea52b89dfa1388b592a260c60a6?s=128

Ragunath Jawahar

February 14, 2020
Tweet

More Decks by Ragunath Jawahar

Other Decks in Programming

Transcript

  1. Re-architecture Ragunath Jawahar • @ragunathjawahar I assume there are tests

    edition
  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)
  3. Where do we start?

  4. None
  5. None
  6. None
  7. None
  8. BOUNDARIES! (Large surface area)

  9. System Boundaries • Network • File System / Database /

    Persistence • Hardware • UI • 3rd Party Integrations
  10. None
  11. None
  12. Network

  13. Network

  14. Network Database

  15. Network Database

  16. Network Database User Interface

  17. + - Network Database User Interface

  18. + - Network Database User Interface Inputs

  19. + -

  20. + - 0.0v

  21. + - 0.5v Network

  22. + - 1.0v Database

  23. + - 1.5v User Interface

  24. + - 2.0v Network User Interface

  25. + -

  26. + - Re-architect

  27. + - + - Re-architect

  28. Approach 1: Cloning Tests

  29. Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  41. None
  42. ff27cb1

  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
  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
  45. Approach 2: Strangulation

  46. + - + - Re-architect

  47. None
  48. Network Database User Interface

  49. + - Inputs

  50. + -

  51. + -

  52. + -

  53. + -

  54. + -

  55. + -

  56. + -

  57. + -

  58. Setup Tear down Test 1 Test 2 Test 3 Test

    4 LoginControllerTest.kt
  59. Tear down Test 1 Test 2 Test 3 Test 4

    LoginControllerTest.kt Setup
  60. Tear down Test 1 Test 2 Test 3 Test 4

    LoginControllerTest.kt Setup
  61. Tear down Test 1 Test 2 Test 3 Test 4

    LoginControllerTest.kt Setup
  62. Tear down Test 1 Test 2 Test 3 Test 4

    LoginControllerTest.kt Setup
  63. Peek: Tests

  64. None
  65. None
  66. None
  67. None
  68. Peek: Migration

  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) }
  70. None
  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) }
  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) }
  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) } }
  74. None
  75. None
  76. None
  77. None
  78. None
  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.
  80. None
  81. None
  82. None
  83. None
  84. Rinse and repeat… Delete the test when you’re done!

  85. None
  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
  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
  88. References https://github.com/simpledotorg/simple-android Approach 1: a5c8ad1 .. ff27cb1 Approach 2: 0c19381

    .. df7788b
  89. Questions?