I use a unit test framework to write tests. These tests take a logical component and test the interface it provides to the rest of the application. I do this for every logical component certainly not every individual class and I certainly don't write a bunch of fake code to pretend like the rest of the app works. I don't care if my code works when provided with a bunch of mocks and stubs I care weather or not my code works with the rest of the system.
The only way to call it unit testing is if you make unit mean something completely different to what most people mean by unit. Yet it is automated testing that covers real functionality.
TDD does not refer only to unit testing, it also includes the integration tests. Indeed, as you say, the value of unit tests is greatly diminished without integration tests. If you're working outside-in your workflow is
- Create integration test/stories
- For each step in integration test
- Add unit tests to make it pass, starting at the view layer and working backwards.
The level of unit testing required for each step depends on the complexity of the code. So what does unit testing get you over just performing integration tests? 2 things:
1) Documents and enforces expected behaviour for edge cases that you haven't written integration tests for - e.g. unusual error conditions
2) Isolates points of inflexion in your system - your stories/use cases will often rely on several components that have multiple logic paths. When you combine the branching logic from several components the combinatorial range of behaviour can be vast. By isolating units at the points of inflexion you are able to test a far smaller range of inputs/outputs so that you can achieve effective test coverage. You're quite correct that the units you tests are not necessarily aligned 1 to 1 with your classes. However, given that a well designed class will have a single responsibility this is often the case.
The only way to call it unit testing is if you make unit mean something completely different to what most people mean by unit. Yet it is automated testing that covers real functionality.