This is always a good question. But to properly answer it, we need to make some assumptions:
- Tests don’t give us a guarantee that our systems will work. It helps, but it is not a guarantee.
- If the tests take too long to run, no one will run the tests
- Good coverage is not synonymous for good tests.
- Tests are good when they are meaningful, when they are test something important. Otherwise, they are useless.
Having said that, what we should test? If we write thousands of integration and/or functional tests, we would test a lot of features, but our build would take too much time to run. It would be easy to developers forget about those tests. So it is not a good approach. We could create thousands of unit tests, the build would be quick and we would have a very fast feedback. It is a good approach, however, it wouldn’t test the classes working together, so it is still not the best approach.
In my opinion, the best approach is the test pyramid.
We still would have lots of unit tests. They are good for testing classes and allow you to use TDD as a tool for Emergent Design. They are not suffiicent though.
We could also create some integration tests to test our classes together (e.g. add tests in the Service Layer). However, we should only add tests that are really necessary since they would take too long to run. The goal would be to test critical features or only happy-path scenarios.
In summary, there are some approaches to test our software. But the most important thing is that our tests should be meaningful and should have value. It should test what is important. The test pyramid approach is a good guide to make us test the right thing at the right level.