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

[RailsConf 2022] Testing legacy code when you dislike tests (and legacy code)

[RailsConf 2022] Testing legacy code when you dislike tests (and legacy code)

Are you supporting legacy code? Would you like to stop? A good testing strategy can transform legacy code into living code that is resilient and easy to evolve.

Learn why legacy code is so difficult to maintain and identify where tests can make the most impact. Not just any tests, though! We'll dive into the characteristics of high-value versus low-value tests and learn techniques for writing tests that minimize the cost of change.

Developers of any experience level can benefit from these concepts. Familiarity with Rails and an automated testing framework is helpful but not required.

58af928b60bb5193e46143497b77c36a?s=128

Maeve Revels
PRO

May 19, 2022
Tweet

Other Decks in Programming

Transcript

  1. @maeverevels #RailsConf2022 Maeve Revels Testing Legacy Code When You Dislike

    Testing (and Legacy Code) betteroff.dev/railsconf-2022-testing-legacy-code
  2. @maeverevels #RailsConf2022 Agenda Questions that deserve answers • What is

    legacy code? • Why should we test it? • When should we test it? • What should we test? • How do we avoid legacy tests? • How do we write high-value tests?
  3. @maeverevels #RailsConf2022 What is legacy code?

  4. None
  5. None
  6. None
  7. @maeverevels #RailsConf2022 Anonymous dev at your old job, probably Code

    originally written by someone else that is currently broken in production.
  8. @maeverevels #RailsConf2022 • 20 years of experience creating legacy code

    • Principal Software Engineer at • Platform team - infrastructure and scalability @maeverevels @maeve railsconf@betteroff.dev Maeve Revels
  9. @maeverevels #RailsConf2022 Brownfield development

  10. @maeverevels #RailsConf2022 Brownfield development 💩

  11. None
  12. @maeverevels #RailsConf2022 Support burden https://xkcd.com/2347/

  13. @maeverevels #RailsConf2022 Risk of the unknown

  14. @maeverevels #RailsConf2022 High cost of change

  15. @maeverevels #RailsConf2022 Legacy code is any code deployed to production

    without tests
  16. @maeverevels #RailsConf2022

  17. @maeverevels #RailsConf2022 Michael C. Feathers, Working Effectively with Legacy Code

    Code without tests is bad code.
  18. @maeverevels #RailsConf2022 Not my code

  19. @maeverevels #RailsConf2022 Not my code please not my code

  20. @maeverevels #RailsConf2022 Why should we write tests?

  21. Support burden Cost Time @maeverevels #RailsConf2022 Go-to-market blog post Weekly

    email newsletter Social media campaign Call to action in application
  22. Risk of the unknown Risk Time @maeverevels #RailsConf2022

  23. Risk of the unknown Risk Time @maeverevels #RailsConf2022 Dev switches

    projects
  24. Risk of the unknown Risk Time @maeverevels #RailsConf2022 Dev surprised

    by git blame Dev switches projects
  25. Cost of change Cost Time @maeverevels #RailsConf2022

  26. Cost of change Cost Time @maeverevels #RailsConf2022 Passed acceptance testing

  27. Cost of change Cost Time @maeverevels #RailsConf2022 Passed acceptance testing

    Tweaks based on feedback
  28. Cost of change Cost Time @maeverevels #RailsConf2022 Passed acceptance testing

    Tweaks based on feedback Changes for large customer
  29. Cost of change Cost Time @maeverevels #RailsConf2022 Passed acceptance testing

    Tweaks based on feedback Changes for large customer 🤞
  30. Cost of change Cost Time @maeverevels #RailsConf2022 Passed acceptance testing

    Tweaks based on feedback Changes for large customer Production incident 😱
  31. Cost of change Cost Time @maeverevels #RailsConf2022 Passed acceptance testing

    Tweaks based on feedback Changes for large customer Production incident Time to write tests…
  32. Cost of change Cost Time @maeverevels #RailsConf2022 Passed acceptance testing

    Tweaks based on feedback Changes for large customer Production incident PM wants new functionality
  33. @maeverevels #RailsConf2022 Time

  34. @maeverevels #RailsConf2022 Gompertz function Time

  35. @maeverevels #RailsConf2022 Gompertz growth model Time • Growth rate of

    organisms • Population dynamics • Market demand for new products • Adoption of new technology
  36. @maeverevels #RailsConf2022 Maintaining legacy code Risk Time

  37. @maeverevels #RailsConf2022 When should we write tests?

  38. @maeverevels #RailsConf2022 Optimize inputs Risk Time f(t) = aeln( c

    a )(1 − e−bt)
  39. @maeverevels #RailsConf2022 Optimize inputs Risk Time b = 0.20 f(t)

    = aeln( c a )(1 − e−bt)
  40. @maeverevels #RailsConf2022 Optimize inputs Risk Time b = 0.14 f(t)

    = aeln( c a )(1 − e−bt)
  41. @maeverevels #RailsConf2022 Optimize inputs Risk Time b = 0.10 f(t)

    = aeln( c a )(1 − e−bt)
  42. @maeverevels #RailsConf2022 Optimize inputs Risk Time b = 0.08 f(t)

    = aeln( c a )(1 − e−bt)
  43. @maeverevels #RailsConf2022 Maintaining legacy code Risk Time

  44. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  45. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  46. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  47. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  48. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  49. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  50. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  51. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  52. @maeverevels #RailsConf2022 Testing legacy code Risk Time

  53. @maeverevels #RailsConf2022 What should we test?

  54. @maeverevels #RailsConf2022 Application behavior

  55. @maeverevels #RailsConf2022 The tests should pass as long as application

    behavior remains unchanged
  56. @maeverevels #RailsConf2022 @maeverevels #RailsConf2022 Seam Model

  57. @maeverevels #RailsConf2022 A seam is a place where we can

    change behavior without changing code in that place
  58. @maeverevels #RailsConf2022 An enabling point is a place where we

    decide to use one behavior or another
  59. @maeverevels #RailsConf2022 Application seams

  60. None
  61. None
  62. @maeverevels #RailsConf2022 Subsystem seams

  63. @maeverevels #RailsConf2022

  64. @maeverevels #RailsConf2022

  65. @maeverevels #RailsConf2022

  66. @maeverevels #RailsConf2022

  67. @maeverevels #RailsConf2022

  68. @maeverevels #RailsConf2022

  69. @maeverevels #RailsConf2022

  70. @maeverevels #RailsConf2022

  71. @maeverevels #RailsConf2022

  72. @maeverevels #RailsConf2022

  73. @maeverevels #RailsConf2022

  74. @maeverevels #RailsConf2022

  75. @maeverevels #RailsConf2022

  76. @maeverevels #RailsConf2022 Object seams

  77. @maeverevels #RailsConf2022

  78. @maeverevels #RailsConf2022 How do we avoid legacy tests?

  79. @maeverevels #RailsConf2022 Sandi Metz, Practical Object-Oriented Design: An Agile Primer

    Using Ruby From a practical point of view, changeability is the only design metric that matters; code that's easy to change is well-designed.
  80. @maeverevels #RailsConf2022 Sandi Metz, Practical Object-Oriented Design: An Agile Primer

    Using Ruby Efficient tests prove that altered code continues to behave correctly without raising overall costs.
  81. @maeverevels #RailsConf2022 Focus on high-value testing

  82. @maeverevels #RailsConf2022 Test value Bugs prevented Initial cost Maintenance

  83. @maeverevels #RailsConf2022 Test value Time Bugs prevented Initial cost Maintenance

  84. @maeverevels #RailsConf2022 Cost(change) > Cost(execution)

  85. @maeverevels #RailsConf2022 The tests should pass as long as application

    behavior remains unchanged
  86. @maeverevels #RailsConf2022 If it is easier to fix regressions than

    it is to fix the test, that test is not worth the effort
  87. @maeverevels #RailsConf2022 How do we write high-value tests?

  88. @maeverevels #RailsConf2022 Integration tests

  89. @maeverevels #RailsConf2022 Integration test value Bugs prevented Initial cost Maintenance

  90. @maeverevels #RailsConf2022 Always create a harness for a happy path

    test
  91. @maeverevels #RailsConf2022 Avoid fixture (or factory) fallout

  92. @maeverevels #RailsConf2022 Use dynamic count assertions

  93. @maeverevels #RailsConf2022 # Don't assert static counts expect(page).to have_content("4 requirements")

    # Get dynamic counts from the data layer instead expect(page).to have_content("#{feature.requirements.count} requirements")
  94. @maeverevels #RailsConf2022 Abstract away selectors

  95. @maeverevels #RailsConf2022 # Avoid direct use of selector logic expect(page).to

    have_css(".card__field i.fa-flag[data- title='International expansion']")
  96. @maeverevels #RailsConf2022 # Avoid direct use of selector logic expect(page).to

    have_css(".card__field i.fa-flag[data- title='International expansion']") # Extract complex selectors into descriptive methods def have_goal_icon(goal_name) have_css(".card__field i.fa-flag[data-title='#{goal_name}']") end expect(page).to have_goal_icon("International expansion")
  97. @maeverevels #RailsConf2022 Functional testing

  98. @maeverevels #RailsConf2022 Functional test value Bugs prevented Initial cost Maintenance

  99. @maeverevels #RailsConf2022 Testing behavior with many known edge cases

  100. @maeverevels #RailsConf2022 Stub or mock external resources

  101. @maeverevels #RailsConf2022 Unit tests

  102. @maeverevels #RailsConf2022

  103. @maeverevels #RailsConf2022

  104. @maeverevels #RailsConf2022

  105. @maeverevels #RailsConf2022

  106. @maeverevels #RailsConf2022

  107. @maeverevels #RailsConf2022 Unit test value Bugs prevented Initial cost Maintenance

  108. @maeverevels #RailsConf2022 Only keep unit tests that are useful to

    you right now
  109. @maeverevels #RailsConf2022 Unit testing techniques can be generalized

  110. @maeverevels #RailsConf2022 Putting it all together

  111. @maeverevels #RailsConf2022 • Legacy code maximizes costs • Testing minimizes

    costs • Focus on behavior over inputs/outputs • Write high-value tests for high-value code
  112. @maeverevels #RailsConf2022 The tests should pass as long as application

    behavior remains unchanged
  113. In memory of Zach Schneider 1992 - 2021

  114. @maeverevels #RailsConf2022 Thank you @maeverevels railsconf@betteroff.dev betteroff.dev/railsconf-2022-testing-legacy-code