Question: Another name for this question could be: How to TDD the main function?

My situation: I'm writing a program from scratch that runs a sort of simulation, and I want to apply TDD to the whole development process. It seems straightforward to me when I think about the small detailed features, but at some point the program needs to *start*. For example, this is a rough outline of how the program will run:

1. Read data from a database.
2. Loop a bunch of times simulating the desired behavior.
3. Write the output somewhere.

I'm tempted to write a test like `testLoopProducesValidOutput` but that's basically just checking to see if the entire program ran correctly, which doesn't seem very unit-like for a unit test.

In the spirit of "don't write code unless you have a failing test", I don't quite understand what kind of test to write to actually get that initial high-level code written that isn't running the entire thing. How would one generally approach this kind of situation?

(Of course one solution is just to not strictly follow TDD for this part, but if there's another solution I'm open to learning before I give up. :) )

Answer: 
> 
> I'm tempted to write a test like `testLoopProducesValidOutput` but that's basically just checking to see if the entire program ran correctly, which doesn't seem very unit-like for a unit test.
> 
> 
> 

You're specifically talking about unit testing, so "let's test the whole lot in one go" is not in the spirit of that. Analyzing this with our unit testing cap on, we can test individual potential points of failure here.

* Does the main logic call [mocked] database dependency to fetch the data?
* When [mocked data] is returned from the [mocked] database dependency, does the main logic call the expected [mocked] simulation dependencies the expected amount of times?
* When a [mocked] simulation dependency returns [mocked data], does the main logic pass [mocked data] into a [mocked] logging dependency?

I've been repetitively explicit about point out that effectively everything *except* the main logic's behavior itself is mocked. These are isolated tests that verify a specific piece of the puzzle. None of these tests verify the entire lot by themselves (that would be an integration test - also valuable but not in scope of what you asked about).

> 
> In the spirit of "don't write code unless you have a failing test"
> 
> 
> 

As with all broad advice, there are cases where you shouldn't take it too literally.

Technically, the advice is possible. If we start from an empty main logic method:

* It doesn't call the database dependency
* It doesn't call any simulation dependencies, let alone the correct number of times
* It doesn't call the logger dependency

However, this is fairly trivial logic and it is silly to mandate that the tests must invariably have been written before you even touched this logic.

Yeah, the advice to write (failing) tests first makes sense in a larger codebase where you're trying to introduce or extend logic. In a trivial logic snippet, the value derived from doing it precisely that way decreases and can become obstructive.  

*(note: I'm not a purist about any design principle and I abhor dogmatic thinking, TDD purists might disagree with this paragraph)*

> 
> Of course one solution is just to not strictly follow TDD for this part, but if there's another solution I'm open to learning before I give up. :)
> 
> 
> 

I think you're overly focusing on the whole "first write failing tests" part. The intention of that advice is not to state that if you do anything else first, that it therefore invalidates the TDD-ness of the codebase. The intention is to point out that you should properly design and back your code using tests, rather than slapping them on at the end of the development process as an afterthought.

Avoid dogmatic thinking. Understand the spirit of the advice rather than the letter of the law. There is no universal piece of advice that does not have an edge case.
I am not a big fan of mocking. It is useful in certain very special scenarios, but if you need a mocking *framework*, in my opinion you are doing something wrong.

Certain things need to be mocked because they cannot (should not...) be done in a unit test. For example accessing the file system to write output files. Or logging into the real production system, or even the real development system.

But your own logic should never be mocked away. Because in the end, you have thousands of unit tests, *and not one of them* actually tests whether your program is working! Every one is testing a tiny bit of functionality packed into a huge mass of mocking. So thousands of test could be green but when actually put together without mocks, it still fails. At least the possibility exists, and the whole point of tests is to make sure there is no such possibility.

Things you need to modify (I'm not saying "mock", because you can just switch out implementations, no need for a mocking framework):

* Logging needs to log into a container that is accessible and constrained to that one test.
* Data input needs to come from a source in that test. For example an in-memopry database unique to that test with data you decide on for each test.
* Data output needs to go into a container that is accessible and constrained to that one test.

So your test should be your actual program, reading from an in-memory database, writing to an in-memory filesystem.

For the first test, it's an empty database. I don't know what that yields, maybe a file with no rows and just the column headers. Assert that. Done. Your first test is written and fails. Make it compile, build and light up green. Then add more.

The point is, do not waste 90% of your testing on building fancy mocks. Test *real* things. Test things that *need* testing, the mocking framework doesn't.

Next test might be with exactly one value in the database. That might yield a file of one row. And then your go to more complicated input and edge cases.

If you cannot build a *public input* for your test case, your program does not need that test case, or that functionality.

As an added bonus, if you test your programs *public interface*, you can refactor your heart out and you do not need to refactor your tests. Because they do what they were designed to do: test whether your program still *works*, not whether you have used those exact functions in that exact order.

To summarize:

* Test *reality*. Not your mocking framework.
* Test whether your program works, not what it does internally.
* Start with the simplest case: no data.
* Then branch out into all the test cases.