Skip to content

Instantly share code, notes, and snippets.

@jameshwang
Last active February 29, 2024 16:51
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jameshwang/5335032 to your computer and use it in GitHub Desktop.
Save jameshwang/5335032 to your computer and use it in GitHub Desktop.
Notes on TDD Patterns

Test-Driven Development By Example: Kent Beck

Test-Driven Development Patterns

  1. Isolate Test: If 1 test breaks, it should be 1 problem to fix. Likewise, if 2 break, then 2 fixes. It shouldn't be that 10 test break and 1 fix fixes them all. Isolating tests encourages you to compose solutions out of many highly cohesive, loosely coupled objects.
  2. Test List: Before beginning, write a list of all the tests. However, this doesn't mean write the tests, it means list them. If you write a bunch of tests all at once, you break the cycle of Red, Green, Refactor.
  3. Test First: write tests before you write code. Writing tests first helps you think about design and also helps you focus
  4. Assert First: try to write your assert/expect/should first. It will show what you want to have pass. Then work backwards to "how" you will make that assert pass. What will you have to setup, mock, stub, etc.
  5. Test Data: Try to use real data that makes sense to the reader. Potentially fixtures and factories
  6. Evident Data: When writing tests, we could potentially go into calculate mode. We could evaluate or calculate long numbers. Don't do this, it could confuse the reader as to the business logic of the process. Example:
BAD EXAMPLE:
bank = new Bank
bank.add_rate("USD", "GBP", STANDARD_RATE)
bank.commission(STANDARD_COMMISION)
result = bank.convert(new Note(100, "USD"), "GBP")
assert(new Note(49.25, "GBP")).equal result

GOOD EXAMPLE:
bank = new Bank
bank.add_rate("USD", "GBP", 2)
bank.commission(0.015)
result = bank.convert(new Note(100, "USD"), "GBP")
assert(New Note(100 / 2 * (1 - 0.015), "GBP").equal result

Red Bar Patterns

These cover when, where, and when to stop writing tests

  1. One Step Test: Pick a test from the list that will teach you something and you're confident you can implement. If the test seem hard to do in one step, break them up into more tests.
  2. Starter Test: When you try to create a test that cover too much like "how do I test that this program identifies faces?" it can be daunting to attack. So create small starter tests such as create a class/method/etc where you give it a picture or nothing at all and it returns data back. The key here is to start with the red/green/refactor feedback loop as quickly as possible. Also, don't get stuck in the loop. If the loop is taking too long, break the tests apart.
  3. Explanation Test: To spread the word of TDD to others without forcing them to convert, try to talk in tests. "So if you have these 2 inputs, it should be this. While if you give me these other 2 inputs, it should be that?"
  4. Learning Test: If you're trying to learn an API, you could potentially write tests to "learn" how the API functions. However, testing your APIs can be a controversial thing. Please be careful of doing this constantly other than for learning.
  5. Another Test: If a new idea comes about while coding/testing, put it on your test list. It's easy to lose focus and momentum. Make sure you capture your great idea without impeding your work
  6. Regression Test: RE-READ!!
  7. Break: If you get stuck, take a break. If you don't know what to type, Fake it (in implementation, it's like just returning a hard-coded number), or use Triangulate. If you still don't know, just break away for a while.

Testing Patterns

More detailed techniques of testing patterns

  1. Child Test: Can't get the large test to pass, break it into child/smaller tests. The key here is to keep the red/green/refactor cycle going. You will get discouraged if you're not going through this loop
  2. Mock Object: If the object you want to test is time consuming, complicated, or etc., mock this object. It also helps in readability because the reader will know immediately what the object can respond to. There is risk because the real objects may not behave like the mocks, but there are techniques to resolve this
  3. Crash Test Dummy: When you need to test exceptions, you may have to simulate/fake scenarios. In the test, just throw the exception so the reader can see clearly that you're testing exceptions.
  4. Broken Test: When working alone and leaving a programming session, stop on the Red/broken test cycle. This will give you an action immediately when you sit back down.
  5. Clean Check-in: When leaving a programming session on a team, leave all your tests running.

Green Bar Patterns

You need to immediately fix a red test. Even if it means bad code/hard coded. You need to go through the cycle!

  1. Fake It ('Til You Make It)*: Even if the function or method returns a hardcoded number, just fake it. This will help turn the test green, but it also helps control the scope of the project. Maybe it returns a constant, but maybe that's all its supposed to return.
  2. Triangulate*: You write 2 or more tests so the code doesn't just return constants like the Fake It pattern tells you to. It helps create a level of abstraction for the code. This technique is really great when you're trying to find the right abstraction/algorithm to fix the problem.
  3. Obvious Implementation*: Sometimes, you need to Fake It or use Triangulate to solve a difficult implementation. But sometimes, you just know how to solve it. In that case, just solve it. The other steps are there when you don't know how to solve it and you need to take small steps to break down the problem and solve it that way. Obvious Implementation is second gear; be prepared to downshift if you get stuck.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment