# Behavior

Start with behavior. Don't focus on structure. 

What matters is some complex operation is supposed to transform some old data to some new data. You can create a test by finding examples of the old and new data.

Where that data is stored, file, DB, or memory, is an implementation detail. It doesn't have to leak into the test. Keep that out and you can change how the data is stored without having to touch the test. Fail to keep that out and the tests will actually make refactoring harder. 

You may find some way to decompose the complex operation into multiple testable steps. If you need that to diagnose errors go for it. Don't feel like TDD demands it though. This may make diagnosing a problem go faster but it locks down implementation details. Now the 3 step complex operation _has_ to be a 3 step complex operation. If you ever figure out how to make it a 2 step operation you'll need to come back and remove some of these tests.

Yes [code katas](https://blog.codinghorror.com/the-ultimate-code-kata/) are simple. But TDD expects you to take your real application and break it down until the part you're testing is simple. Then you build on that by adding more and more. Yes, that will change the code you can write. That's the point.

Now that said, some dry boring structural code doesn't need to be wrapped up in a test of its own. Test the interesting behavior.

# Mocks

Does that mean never mock? No. Tests need the "unit under test" ([which is not necessarily just one class][1]) to be [unit testable][2]. That is, the unit should be fast (run-on-every-compile fast), and deterministic (always does the same thing), always ready for testing (no configuration magic) and should not care about whatever else is running (parallelizable). The best argument for mocking is that the unit won't be those things without the mock.

Another argument often made for mocking is to confirm that something was called. This is thorny because sometimes it's right and sometimes it's wrong. If we're testing behavior it's none of the tests business how the unit gets it's work done. Period. Full stop. Except... well sometimes that method call *is* the behavior of the unit. What gives?

Many tests are written in a strictly functional style. Input goes in the arguments. Output gets returned. And side effects are evil! Avoid at all costs!

But TDD is used in codebases that aren't purely functional. And sometimes, just sometimes, a unit [doesn't return it's output](https://softwareengineering.stackexchange.com/questions/365829/return-considered-harmful-can-code-be-functional-without-it). Instead, it calls a method on an [output port](https://softwareengineering.stackexchange.com/questions/357052/clean-architecture-use-case-containing-the-presenter-or-returning-data/357066#357066). One way to test such units is to mock that [output port](https://softwareengineering.stackexchange.com/questions/370052/unit-testing-in-viper-architecture-possible/370055#370055).

Those are my two excuses for mocking: to improve the testability of the unit or to capture the output to test. Being mockable is not a good excuse to mock.  

# Granularity

But some say: every class has an interface. Every interface should be tested. And in isolation or it's really an integration test.

Then I say: I've worked in shops that insist on this. I understand the urge to not trust developers to do testing properly and the desire to have rules that are easy to verify. However, us lazy programmers are often smart enough to realize that in such an environment the lazy thing to do is to just not create many classes. Solve the problem procedurally and you can avoid writing the explosive number of tests this philosophy would demand. No static analysis tool will ever catch you deciding against extracting a class because your shop made it too much of a pain. In short, there is no substitute for developers who care about doing this right. Inspiring us to care always works better than demanding we conform.

This isn't to say you can't test very granular behavior. I'm just saying the way to identify that behavior isn't by ensuring every single class has a single test class. Sometimes a class is the interface for many classes.

# Types of tests

As for the unit vs integration test distinction, I've seen them defined many ways. The most useful definitions will give you two separate piles of tests. Fast ones that you can run with every compile and slow ones that you can run with every merge. Keeping those separate is important because nothing ruins a fast test suite like a slow test. I don't really care what you call them.

# The point of a test

A passing test should make you feel like you can trust the unit to behave. You should feel like you don’t need to read it. You should feel like you trust it. That should free you to focus on the suspect code. Write tests that will make you feel that way. 

# Conclusion

TDD isn't everything. There are many other successful ways to develop. And you can successfully mix them. But if you feel like TDD only works on toy katas you need to play with it more.


  [1]: https://martinfowler.com/bliki/UnitTest.html
  [2]: https://www.artima.com/weblogs/viewpost.jsp?thread=126923