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

Effective testing: how to confidently deploy on a Friday

Effective testing: how to confidently deploy on a Friday

Have you ever worked on a codebase that had a high test coverage, yet regularly had bugs slip into production? This can
lower our confidence in our tests, and make us question why we even bother with them. In this talk, we'll cover how to
write tests that get us the most value and confidence for the least effort.


Mohamed Oun

October 23, 2019


  1. Effective testing Mohamed3on How to write tests that let you

    confidently deploy on a Friday By Mohamed Oun Software Engineer at HelloFresh mohamed3on.online
  2. Tests when you make a one-line change

  3. Why do we even write tests?

  4. We write tests to give us more confidence The point

    of tests is NOT to tell you that you made changes, it’s to tell you whether your changes broke something
  5. If we want developers to embrace a culture of testing,

    we have to show the benefits to it
  6. Tests should serve as a documentation for how the code

    works • Think about clear names for your test cases • Be explicit about what you’re testing • Avoid DRY test code, It’s ok to duplicate test objects and code to make clearer, easier to read test cases
  7. Each test should bring unique value and should not be

    coupled to any other test
  8. Tests should be as flexible and accommodating as possible Think

    hard of the core functionalities you don’t want to break, and focus on testing those
  9. Thinking flexibly, how do we test this?

  10. Two ways to test for this • Ensure that all

    recipe cards are 170px height (current default size) • Ensure that all recipe cards have the same height (more flexibility)
  11. Fix your bugs by writing tests for them 1. Find

    out the buggy function that’s returning a wrong value for some inputs (easier said than done). 2. call the function with those inputs in a test. 3. Use that test to reproduce the bug and fix it. Why? 1. Rerunning that test is much faster that reproducing manually over and over. • Faster feedback loop leads to faster bug fixes 2. Having that test in the test suite ensures this bug never happens again
  12. How do I know if my test is bad? •

    If your test broke when your source code didn’t, it’s a bad test • If your source code broke when your test didn’t, it’s a bad test
  13. Tests should be behaviour-sensitive, but structure-insensitive

  14. What shouldn’t break your tests • Adding a data-test-id •

    Refactoring without changing functionality • Changing styles* (fonts, colours, paddings) * unless it’s a reusable UI component (example: Design System)
  15. A tale of two tests

  16. None
  17. None
  18. Avoid snapshots as much as you can • They’re implicit,

    so it’s hard to know exactly what the test is for • They’re usually brittle, breaking when the component’s functionality doesn’t • They almost always fail the (bad test) test • Can be helpful in the case of reusable UI components (ex. design system components)
  19. Many of your unit tests can be replaced by a

    type system* *Not sponsored by Microsoft
  20. None
  21. VS

  22. 100% test coverage is a trap “When a measure becomes

    a target, it ceases to be a good measure.”
  23. High test coverage is NOT an indicator that your tests

    are sufficient • Focusing on high test coverage leads to low-quality, easy-gain tests (hint: snapshots) • you get diminishing returns on your tests as the coverage increases much beyond 70-80% • Very high coverage means that you are likely testing implementation details, which means your tests will probably break while refactoring, ironically making it harder, not easier, to refactor your code.
  24. OK, so how do I know if my test suite

    is good enough? • You rarely get bugs that escape into production. • You are rarely hesitant to change some code for fear it will cause production bugs.
  25. When you have bugs in production but tests are green

  26. Black-box testing Testing the public API, from the point of

    view of your users
  27. React components have 2 main users The developer using the

    component in another component, and the user seeing the rendered output
  28. • Clicking on an interactive element (end user) • Changing

    props for a component (developer user) • Instead of testing component state/methods, testing the rendered output Black box testing in UI
  29. How to test this counter?

  30. None
  31. Better yet, use data- test-id

  32. How do I know if I’m testing implementation details? •

    Imagine you didn’t have access to the source code of what you are testing, would you still be able to write that test?
  33. Unit? Integration? End to end?

  34. How to know if my test is unit, integration or

    E2E? • If you mock everything, it’s a unit test • If you mock nothing, it’s an end to end test • Everything in between is an integration test
  35. Unit → integration → E2E Trade-offs 1. Points of potential

    failure 2. Engineering time
  36. Unit → integration → E2E 1. Amount of code tested

    2. Confidence 3. Testing implementation details Trade-offs
  37. None
  38. Testing tools • Cypress/Selenium: • Integration ↔ E2E • Run

    in a real browser • Can still mock API responses • Jest: Enzyme/React-testing-library: Unit ↔ Integration • Run in an emulated browser • Can mock almost everything (functions, components, API responses)
  39. Tired: Testing pyramid

  40. Credits: @mrjedmao Wired: Testing trophy

  41. Summary • Prefer explicit over implicit tests (avoid snapshots in

    most cases). • Use tests to fix the bugs you find • Use test coverage as a guide, not as a goal. • Prefer integration tests to get the most confidence and value out of your tests (mock as few things as possible). • Write tests that mimic real world usage of your application, and avoid testing implementation details (black-box testing). • Use a type system if you can.
  42. Thank you!