Is it worth implementing service integration tests in Spring Boot application? - spring

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?

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.

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:
https://martinfowler.com/articles/practical-test-pyramid.html
https://www.baeldung.com/spring-tests

Related

Unit testing for Spring Boot API

I'm developing a Spring Boot Web API, and I'm currently writing the required units tests.
I was wondering : Isn't writing the units tests (JUnit + Mockito) for the controllers sufficient ? Since the controllers are the entrypoint of my application and that all the logic that is implemented within the Service side is called from the exposed API, why do I need to write test for the Service side ?
First of all, if you write your tests to cover "required level of tests" or requirement to "have some tests at all" having the production implementation already done, it is slightly too late. In the majority of cases having tests first, based on your requirements, contract, use case or anything it more optimal approach. Nevertheless, I don't know your situation and the thing you're trying to implement, so treat it as a suggestion and move on to the key thing you are asking about.
Your JUnit (preferably 5) and Mockito tests, which probably use MockMvc are very good unit(-like) tests to cover web communication concerns such as: HTTP request types, content type, encoding, input and output parameters JSON (de)serialization, error handling, etc. They are best to test with the service layer mocked. Thanks to that you can easily cover a lot of web cases without the need to prepare data in a database, etc.
The core logic has to also be tested. Depending what it is, it might be feasible to test it in the unit way (the easiest to write, can cover a lot of - also corner - cases). It could be supplemented with some set of integration tests to verify that it works fine also in integration (Spring Beans, DB, etc.).
If desired, you may also write some E2E test from the web call via (real) HTTP requests through controllers, services to a database/datastore (if any), but I would limit it only to the most important scenarios to use it in your CI/CD pipeline to verify that a deployment finished successfully.
Disclaimer. I've found this approach useful in multiple situations, but definitely in some other circumstances it might be good to change the balance point to better apply testing.
I think you are probably getting confused between unit and Integration tests.
If you are using Mockito you are probably referring to unit tests wherein, the scope of the Test Class should be only the current class.
Any external method calls should be mocked.So in your case the service calls should be mocked in case you are writing unit test for your controller class.
Your Test Suite should contain
Junit for Controller-- To Test the interface contract points like HTTP Method, Request Parameters, Mandatory inputs, Valid Request Payloads.
Junit for all other classes including Service classes- This is to test your application classes core logic
Integration Test- Finally an integration test which can hit your controller endpoints with service classes and rest of the application code functionality.

TDD: how to test data persistence abstraction

problem statement
Let's say I want to implement a simple key-value storage, which provides basic CRUD operations. The only requirement is that all the modifications should persist between application launches. I'm not concerned about particular persistence mechanism, as it may change during development, or even be different depending on the platform.
the question
How should I approach testing such library / class / module (whichever it becomes in the end) ?
All the answers I found were focused on testing particular database solution, but that's what I want to avoid. I'm only concerned about the fact that changes are being persisted, not about how they're persisted.
Solutions I considered:
extract the actual persistence implementation, and test each implementation separately, and only test if the abstraction layer calls correct methods
The problem I see with this approach is large amount of code for very simple task, and the fact I'm still concerned about particular persistence mechanism in the end. This may complicate development, especially for such a simple thing as a key-value storage.
make some test that actually launches the application multiple times, and checks if the data changes are persisted between launches
That would test exactly what I need, but such a test would probably be expensive, and not necessarily easy to write.
Test only if the methods work for single process, do not test persistence at all
Well, the whole point of the library would be to test persistence. Say, some user settings, or game save. Testing if it works in memory doesn't test the real problem.
Maybe some other solution, I didn't think of?
I've read through most of the topics related to persistence and TDD here, and on other sites, but all I could find focuses on specific persistence mechanism.
somewhat related, but doesn't touch on the testing subject:
How many levels of abstraction do I need in the data persistence layer?
A persistence layer is a port of your application. As such, your approach should be to write an adapter to that port, and test that adapter using an integration test.
The test itself doesn't need to spin the adapter more than once - testing that things actually persist would be testing the persistence mechanism itself, and that's not your concern. At some point, you'll have to assume things work, and they have their own tests to make sure that's correct. A typical write and then read back test would do for most cases.
Once the adapter exists, you can use several techniques for writing other tests - mock the adapter when it is a collaborator for other units, or use an in-memory implementation of the adapter (using a contract test to see that the in-memory implementation is the same as the other one).

Junit test that persist in mysql

Is it possible to write junit test that persist in mysql using hibernate / jpa?
If so any example available?
I'm using spring/hibernate for my application
When you have real database connection I would not call your test unit test, but more like integration test. To be honest this kind of tests is not a good idea in most cases. It requires some maintenance (every time you have changes in db), and in most cases just tests if it is possible to connect do database (and save some simple object).
Focus on writing good tests for domain level classes. Simple database integration tests will only give you illusion of high quality application.
If you want to write an integration test that looks like a unit test, you could try Arquillian.
What Arquillian does is basically start an application container (Glassfish I think is the default), then it deploys your server-side application in the container and runs the tests against the just-deployed application.
What you write in the unit tests is really client code, so that what you are effectively running is an integration test (with a real database and all the services you would have in a real environment), just in a junit-like way.
They also have a specific tutorial for persistence testing.

Unit testing in spring mvc

If I want to unit test my dao classes in spring would I just call my service methods and test those or would you test the service methods separately to the actual dao methods?
Also should I mock the dao calls or actually use an in memory database like H2? I see that as more of an integration test although some tutorials do this, or would a standard approach be to test with mock database objects for the service tests and use H2 when testing the dao calls?
Finally.. My application has a rest API which is called from the web front end using the Spring rest template and so only the API web app accesses the database.
Would I test the rest methods in each web app using mocked objects and then Start a tomcat instance and integration test between the 2 apps? If I used tomcat and ran integration tests between the apps would connect up a database or mock objects in the API app?
Testing the rest calls from the web app relies really on how the API app's rest method responds so is this even worth testing in isolation?
I find unit testing quite confusing as some of it seems almost to be integration testing.
Does it matter if you run integration tests against H2 in memory but then in reality I would be using MySQL?
Trying to answer your questions in the order asked...
For unit testing DAO methods, you should test the actual DAO classes directly with a database in a known state. H2 is great for this, since you can run it without setting up MySQL for each test. Utilizing setup methods with the #Before annotation is great to make sure that the database will respond in expected ways.
For unit testing Service classes, you should mock the DAO classes, so that they will always behave in expected ways. If you use your service and DAO classes with actual data, you are now running integration tests, by testing multiple layers simultaneously. Both have their value, though is generally best to unit test before integration testing, to make sure each component is functioning.
The same goes for testing your controller, you should unit test it and mock the service classes, and then perform integration tests with mock requests to test request/response scenarios. Again, with this test setup you are now testing many layers and classes simultaneously. This is great, because it gives you a good idea of how your application will function in reality, but is not useful for isolating bugs.
H2 and MySQL obviously are not the same, and don't share all the same functionality, so you can't say with 100% confidence that an H2 test will pass in MySQL, but if you are just testing standard CRUD operations, it should do the trick.

In TDD, why OpenEJB and why Arquillian?

I'm a web developer ended up in some Java EE development (Richfaces, Seam 2, EJB 3.1, JPA). To test JPA I use hypersonic and Mockito. But I lack deeper EJB knowledge.
Some may argue that we should use OpenEJB and Arquillian, but for what?
When do I need to do container dependent tests? What are the possible test scenarios where I need OpenEJB and Arquillian?
Please enlighten me :)
There are two aspects in this case.
Unit tests. These are intended to be very fast (execute the whole test suite in seconds). They test very small chunks of your code - i.e. one method. To achieve this kind of granularity, you need to mock the whole environment using i.e. Mockito. You're not interested in:
invoking EntityManager and putting entities into the database,
testing transactions,
making asynchronous invocations,
hitting the JMS Endpoint, etc.
You mock this whole environment and just test each method separately. Unit tests are fine-grained and blazingly fast. It's because you can execute them each time you make some important changes in code. If they were more complex and time-consuming, the developer wouldn't hit the 'test' button so often as he should.
Integration tests. These are slower, as you want to test the integration between your modules. You want to test if they 'talk' to each other appropriately, i.e.:
are the transactions propagated in the way you expect it,
what happens if you invoke your business method with no transaction at all,
does the changes sent from your WebServices client, really hits your endpoint method and it adds the data to the database?
what if my JMS endpoint throw an ApplicationException - will it properly rollback all the changes?
As you see, integration tests are coarse-grained and as they're executed in the container (or basically: in production-like environment) they're much slower. These tests are normally not executed by the developer after each code change.
Of course, you can run the EJB Container in embedded mode, just as you can execute the JPA in Java SE. The point is that the artificial environment is giving you the basic services, but you'll end with tweaking it and still end with less flexibility than in the real container.
Arquillian gives you the ability to create the production environment on the container of your choice and just execute tests in this environment (using the datasources, JMS destinations, and a whole lot of other configurations you expect to see in production environment.)
Hope it helps.
I attended Devoxx this year and got a chance to answer the JBOSS dudes this question.
Some of the test scenarios (the stuff i managed to scribble down):
Configuration of the container
Container integration
Transaction boundaries
Entity callback methods
Integration tests
Selenium recordings

Resources