I have come accross multiple articles on integration testing on Spring Boot applications. Given that the application follows three layer pattern (Web Layer - Service Layer - Repository Layer) I have not seen a single article with integration testing the application up to just the service layer (ommiting the web layer) where all the business logic is contained. All of the integration tests seem like controller unit tests - mostly veryfing only request and response payloads, parameters etc.
What I would like however is to verify the business logic using service integration tests. Since the web layer is responsible only for taking the results from services and exchanging them with the client I think this makes much more sense. Such tests could also contain some database state verifications after running services to e.g. ensure that there are no detached leftovers.
Since I have never seen such a test, is it a good practice to implement one? If no, then why?
CodePudding user response:
This is borderline opinion-based, but still, I will share my take on this. I usually follow Mike Cohn's original test pyramid such as depicted below.
The reason is that unit tests are not only easier to write but also faster and most likely cover much more than other more granular tests.
Then we come across the service or integration tests, the ones you mention in your question. They are usually harder to write simply because you are now testing the whole application and not only a single class and take longer to run. The benefit is that you are able to test a given scenario and most probably they do not require as much maintenance as the unit tests when you need to change something in your code.
However, and here comes the opinion part, I usually prefer to focus much more on writing good and extensive unit tests (but not too much on test coverage and more on what I expect from that class) than on fully-fledged integration tests. What I do like to do is take advantage of Spring Slice Tests which in the pyramid would be placed between the Unit Tests and the Service Tests. They allow you to focus on a specific class (a Controller for example) but they also allow you to test some integration with the underlying Spring Framework or infrastructure. This is for me the best of both worlds. You can still focus on a single class but also test some relevant components of your application. You can test your web layer with @WebMvcTest
or @WebFluxTest
(so that you can test JSON deserialization and serialization, bean validation, etc...), or you can focus on your persistence layer with @DataJpaTest
, @JdbcTest
or @DataMongoTest
(so that you can test the actual persistence and retrieval of data).
Wrapping up, I usually write a bunch of Unit Tests and then web layer tests to check my Controllers and also some persistence layer tests against a real database.
You can read more in the following interesting online resources:
CodePudding user response:
There is no one true proper way to test Spring applications. A general approach is as you described:
- slices tests (
@DataJpaTest
,@WebMvcTest
) etc for components that heavily rely on Spring - unit tests for domain classes and service layer
- small amount of e2e tests (
@SpringBootTest
) to see if everything is working together properly
Spotify engineers on the other hand wrote how they don't do almost any unit testing and everything is covered with integration tests that covered with integration tests.
There is nothing stopping you from using @SpringBootTest
and test your service layer with all underlying components. There are things you need to consider:
- it is harder to prepare test data (or put system under certain state), as you need to put them into the database
- you need to clean the database by yourself, as (
@SpringBootTest
) does not rollback transactions - it is harder to test edge cases
- you need to mock external HTTP services with things like Wiremock - which is also harder than using regular Mockito
- you need to take care of how many application contexts you create during tests - first that it's slow, second each application context will connect to the database, so you will create X connections per context and eventually you can reach limits of your database server.