I need to know if testing only restControllers in spring boot application is enough in unit testing or better to test all the classes independently?.
There is no simple and 'right' answer. You should decide it for yourself.
First of all it worth to mention two things:
Framework for testing controllers: Spring MockMvc framework, it is for integration testing of Spring web applications. It can test single controller (with mocked service layer) and entire application (including service and database layers, with embedded databases).
Article answering which tests you should have: Test pyramid conception (in short you should have a lot of unit tests and a little of integration tests).
My personal preference is to cover the 'happy path' by integration tests (covering service and DB layers).
Then I cover by unit tests classes with complex logic (not all classes).
In my practice most of applications are quite simple, and I found that I have a lot of integration tests and not so many unit tests.
Max Fariskov is right, and I can add two remarks.
Unit tests work properly when you launch them really often. After
every compile is often enough :). That's why every unit test must chek one little part of the functionality. Testing the whole controller is a too complex task.
Decouple business logic from a controller,
Put it into service,
Test service with its own unit test
Mock service functionality in your controller - test and check only
controller behavior.
Unit tests are fast but the integration tests are slooow and complex. So it is a good idea to split them into different groups. Execute your unit tests often and your integration tests only when you have to.
I'm doing it with "profiles":
Create two empty interfaces. For example:
public interface UnitTest {}
public interface IntegrationTest {}
Mark your test classes with proper annotation
#Category({IntegrationTest.class})
public class MySlowAndComplexServiceTest {}
#Category({UnitTest.class})
public class MyFastServiceTest {}
create two profiles in you .pom
unitTests
true
maven-surefire-plugin
2.19.1
UnitTest.class
integrationTests
false
maven-surefire-plugin
2.19.1
IntegrationTest.class,UnitTest.class
It's done.
Your unit test will be executed by default and you can switch on "integrationTests" profile to activate you integration tests at any moment.
Related
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
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.
I am not good at English. Please understand.
I knew. The use of #SpringBootTest() Annotation to load the entire Bean was clearly thought of as an "Integration Test."
However, the use of #SpringBootTest(classes=mybean.class) was thought to be "Unit Test" because it only runs a certain (selected) "Bean(Class)" although it runs a spring.
I also thought the way to use #ContextConfiguration" was also "Unit Tests." Because I thought only certain classes could be executed, such as #ContextConfusion(classes=myBean.class).
But it was my mistake. I received an answer to the last StackOverflow.
#SpringBootTest vs #ContextConfiguration vs #Import in Spring Boot Unit Test
My last question-->
Whether using #ContextConfiguration can be a "Unit Test"
$$$answer --->
No, it cannot, it's an integration test that runs only one class in spring.
Usually, we don't run only one class with Spring Framework. What is the benefit of running it inside the spring container if you only want to test the code of one class (a unit)? Yes, in some cases it can be a couple of classes, but not tens or hundreds.
If you run one class with spring then, in any case, you'll have to mock all its dependencies, the same can be done with mockito...
So I ask again.
Is "#WebMvcTest" also "Integration Tests"? I thought "#WebMvcTest" was the "Unit Test." However, based on the last answer, #WebMvcTest also has spring running. So the logic of the last answer is that "#WebMvcTest" is also "Integration Test," but I still don't understand.
I'm confused. Am I just playing with words?
Unit Test vs Integration Test...
Is the #WebMvcTest also an "Integration Test"?
Quote from Spring doc:
True unit tests typically run extremely quickly, as there is no
runtime infrastructure to set up. Emphasizing true unit tests as part
of your development methodology can boost your productivity.
Spring is also a kind of runtime infrastructure .So unit test should run without requiring to involve other frameworks even Spring.
Then it has the whole section to talk about Integration testing by introducing Spring TestContext Framework which it describes as :
The Spring Framework provides first-class support for integration
testing in the spring-test module. The name of the actual JAR file
might include the release version and might also be in the long
org.springframework.test form, depending on where you get it from (see
the section on Dependency Management for an explanation). This library
includes the org.springframework.test package, which contains valuable
classes for integration testing with a Spring container.
#ContextConfiguration is come from the Spring TestContext Framework while Spring Boot builds on top on it to provide #SpringBootTest and #WebMvcTest etc. So all these stuff are for Integation testing.
So to summarise, if a test requires starting up Spring in order to run such as #WebMvcTest , it is not a unit test but an integration test.
Firstly, some definitions taken from the Software testing fundamentals web site:
UNIT TESTING is a level of software testing where individual units/ components of a software are tested. The purpose is to validate that each unit of the software performs as designed.
INTEGRATION TESTING is a level of software testing where individual units are combined and tested as a group. The purpose of this level of testing is to expose faults in the interaction between integrated units.
To answer your question, the three annotations #SpringBootTest, #ContextConfiguration, and #WebMvcTest are all involved in loading a subset of Spring components. They are in consequence related to integration testing.
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.
I have two applications. I did integration unit tests for one of these applications, but the services that call the other application are mocked up (instead of injecting the real service, I inject another one which is mocked up).
Is there a possible way to make a real connection to the other application without having to mock it up.
A simple example would be really helpful.
Thanks in advance!
just inject the real services and do your integration test. The issue is to make sure that everything that everything that needs to be injection can be injected. Lets call your services foo and bar where foo depends on bar. if bar depends on something in the application server then starting it up during a unit might be a problem, since you are not running the app in the application server.
Integration testing is important and valuable, but it requires some careful thought to setup. The way that I have managed to setup integration testing in my application is to use spring profiles to seperate the combinations of configuration. For example I have profiles called.
production
development
container
standalone
So that way you can have a test that is launched with the proper profile that setups all the correct beans to be injected like so.
#ActiveProfile(profiles={"deveolpment","standalone"})
#RunWith ... etc other spring annotations to configure a test
public class SomeJunitTest {
}
Using profiles makes it very easy to have fine grained control over which set of beans are configured for each test.
Also for integartion testing I have found TestNG to be much easier to use that JUnit because it has features that make writing integration tests easier.