TDD Where Did It All Go Wrong - egnomerator/misc GitHub Wiki

TDD, where did it all go wrong? (video here)

Summary Points (made at the end of the video)

  • The reason to test is a new behavior, not a method or a class
  • Write dirty code to get green, then refactor
  • No new tests for refactored internals and privates (applies to both methods, classes)
  • Both develop and accept against tests written on a port
  • Add integration tests for coverage of ports to adaptors
  • Don't mock internals, privates, or adapters

Topic Agenda

  • The Problem
  • TDD Rebooted
  • Red-Green-Refactor
  • Clean Code When?
  • Refactoring to Patterns
  • Ports and Adapters
  • Gears
  • ATDD
  • Behavior Driven Development
  • Mocks
  • Q&A

The Problem

  • We often write more test code than production code
  • Refactoring could commonly break tests--especially tests that use Mocks; have to re-write the Mocks
    • the tests were an obstacle and increased effort required
  • Sometimes difficult to understand purpose of a test that you haven't seen in awhile
  • Large test base can increase cost of the check-in dance

TDD Rebooted

  • ref: "Test-Driven Development" by Kent Beck
  • TDD as originally intended was great
  • perhaps where we've gone wrong is adding later practices on top of it
  • the kent beck book address a lot of the issues brought up above

TDD

  • Avoid testing implementation details, test behaviors
  • a trigger for a new test is a new requirement--not a new method or class
  • test the public API--the stable requirements
    • how you implement it may change
  • the SUT is not a class, it is the "exports" from a module--its facade
  • refactoring is key to separating implementation details you don't need to test from behavior you do need to test

Red-Green-Refactor

  • Red
  • Green: speed trumps design
  • Blue
    • this is when we produce clean code, remove duplication, sanitize code smells
    • this is when you apply patterns
    • you do not write any new tests in this phase
    • you do not change behavior in this phase
    • you improve the code and stay green
      • i.e. you improve the code while maintaining the required behavior
      • you might extract out a new class in this phase--DON'T write a test for that class!

Clean Code When?

Refactoring to Patterns

Ports and Adapters

Gears

  • in the analogy, refactoring is like a high gear--implementation is clear, you just write it
  • but if you are refactoring significantly, and you feel you need tests to help you, write them
    • in the gear analogy, this is shifting down a gear
  • when you have finished refactoring, DELETE those tests
    • because those tests were about implementation details

You want a testing pyramid, not a testing ice-cream cone

  • cone: a mass of manual testing, on a lot of Auto-GUI tests, on less integration tests, on even less unit tests
    • this is not scalable or elastic to change
  • pyramid: a large base of unit tests, under key integration tests, under minimal UI tests

Hexagonal Architecture

  • overall
    • POCOs in the middle, surrounded by an adaptor layer, ports in between
  • ports layer
    • a port is a door
    • a port is a use-case boundary--behavior driven--so test this
  • adaptor layer around the center made of POCOs
    • knows how to talk to the outside world--dbs, networks, etc.
    • e.g. web frameworks like web forms
    • don't test these directly
    • integration tests to cover communication between adaptors and ports

ATDD

  • customers don't care about these front-facing tests
  • but they may have been what taught us to focus on behavior in unit tests

Behavior Driven Development

Just a clarification:

BDD is a separate discipline from the TDD concept of testing behavior

Mocks

Don't use mocks to "confirm" implementation details

  • this is a common thing senior devs do to "enforce" correct behavior on jr devs
  • don't do this

A note on IOC containers--try to avoid them or use them sparingly

Q&A

⚠️ **GitHub.com Fallback** ⚠️