Unit, integration, end-to-end tests: do I need all of them?
Yes. I mean, don’t even think about it. You’ll need all of them, probably in different measures, but there is no “we shipped to production without tests”.
Tests are the first rampart separating you from madness and failure.
Why madness? Try to do even a small refactoring after you’ve deployed your app. Without automatic tests you’ll have to manually probe the entire system (or systems if you’re on microservices).
Why failure ? Simple, just think on the long run. Maintenance will quickly become a hell and adding new features will soon bring you to the infamous “it’s better if we re-build this from scratch”.
So! Where should we start? From the pyramid!
Starting from the bottom, you’ll begin with writing the unit tests. “Unit” here means that you’re testing a single small atomic piece of your system, a class, a function, whatever. You won’t connect to any external resource (eg. database, remote services) and you’ll be mocking all the dependencies.
So, ideally you’ll be checking that under specific circumstances a method is throwing an exception or the cTor is populating the class properties or the result of a computation is a specific value giving a controlled input.
Also, unit tests have to be extremely fast, in the order of milliseconds, giving you a very quick and generic feedback of your system.
Next is the “Service” layer or, more commonly, “Integration”. This is where things start to get interesting. Integration tests check that two or more pieces fit correctly and the cogs are oiled and greased. So stuff like your Persistence layer, access to the database, ability to create or update data and so on. They might take more time than unit tests and probably will be in a lesser number, but their value is extremely high.
Then we have the “UI” or “end-to-end” tests. Here we’re making sure that the whole system is working, inspecting from the outside, with little to none knowledge of the inner mechanism. You’ll be checking that your API routes are returning the right HTTP statuses, setting the proper headers and eating the right content types.
In the end it’s all a matter of perception. The point of view is moving from the inside of the system, the developer perspective, to the outside: the consumer perspective.
There are of course other typologies of tests, acceptance, smoke, functional and so on. But if you begin adding the coverage using this pyramid you’ll save an awful lot of headaches and keep your system maintainable and expandable.