If you have done or are still doing "TDD" the way I used to naively do it, you wrote your tests first, but they were painful to write. It seemed like you had to write a mile of tests to feel like you covered all the representative conditions for a test case. This is because your units were/are too big and encompass the interactions of multiple concerns.

When you start to really get how TDD and separation of concerns works, tests are much smaller, easier to write, and give you high confidence that all your units work as designed. Then, a funny thing happens though. You notice that although your confidence in the units is higher, your confidence in the system is not so high - sure enough, when you try to run it, there are all kinds of bugs in the interactions between the units. That's because these are interactions are part of what used to be covered by those miles of tests you wrote when you were doing it wrong.

What's missing at this point is simply IntegrationTests. Integration tests are not necessarily done with different tools than UnitTests, but they should be kept distinct from them, in a different directory or package for instance. Often, an IntegrationTest will correspond with some kind of Facade that also has a unit test using mocks and/or stubs, the difference being that the integration test does not do the mock/stubbing.

You might be asking at this point, doesn't this just give us back the same pain we had before? Lots of combinations to test? It turns out that it does not. If the units are well covered alone, then it only takes a few tests of an interaction to have very high confidence that the integration as a whole is solid.

So when do we write the IntegrationTests? Honestly, this author still needs to research/experience more to answer confidently, but it appears that they should be written before the tests of the units that will be integrated. The IntegrationTests can be left in a failing state while the units are built and tested. As usual, the UnitTest suite's green bar should return to green after each test is made to pass. The IntegrationTest suite's bar should go green once all of its required units have been completed.

Usually, people speak/write about IntegrationTests, they refer only to testing with real databases, real I/O, or something else that's slow and involves components outside of your code itself. Certainly, those are IntegrationTests and they're important. They're just not the only IntegrationTest cases you need to be concerned with. You also need to test that the units within your code work together properly when integrated.