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 katas 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.

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, 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 a need to mock 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) at all. 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 make the unit testable or to capture what needs to be tested. Being mockable is not a good excuse to mock.  

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 some more.


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