It's not really about the 'e2e' test covering it all, which btw is the wrong label -- don't know how NestJS gets this wrong.
A unit test does not have to be small, it can be a function, class, a component, a system, as long as it meets the following requirements:
- Can be run in parallel
- Does not touch fs/database/network or anything that makes it slow
- Does not change the environment, e.g. configuration.
I don't like to talk about unit or integration tests, a test is either fast or slow. So I will continue with this terminology.
Depending on the complexity of your app and the state you are in, you might prefer slow tests, in specific, acceptance tests. They will cover the most code and give you a good amount of confidence. The problem however, is that this does not scale the moment you app gets more complex.
Speaking from a TDD perspective, we aim for short cycles. Every change should tell us whether we broke something without having to wait for the tests to be ran. This is definitely not possible with slow tests, they are at average 300ms+, where a fast test runs within 1ms.
Now when do you write such tests? In reality, as much as possible. Let the slow tests confirm that the core functionality works, and your fast tests can also verify this behaviour plus more. The harder part about it is understanding the dependencies you are marrying in your tests. Have you ever heard a developer say, well, if I have to refactor... I have to throw away my "unit tests"? That's because they failed to understand the underlying relationships within the system under test.
Hope this helps you 🙂