Unit tests vs integration tests with Spring - java

I'm working on a Spring MVC project, and I have unit tests for all of the various components in the source tree.
For example, if I have a controller HomeController, which needs to have a LoginService injected into it, then in my unit test HomeControllerTest I simply instantiate the object as normal (outside of Spring) and inject the property:
protected void setUp() throws Exception {
super.setUp();
//...
controller = new HomeController();
controller.setLoginService( new SimpleLoginService() );
//...
}
This works great for testing each component as an isolated unit - except now that I have a few dozen classes in the project, after writing a class and writing a successful unit test for it, I keep forgetting to update my Spring MVC context file that does the actual wiring-up in the deployed application. I find out that I forgot to update the context file when I deploy the project to Tomcat and find a bunch of NullPointers from non-wired-up beans.
So, here are my questions:
This is my first Spring project - is it normal to create unit tests for the individual beans, as I have done, and then create a second suite of tests (integration tests) to test that everything works as expected with the actual application context? Is there an established best practice for this?
In addition, how do you separate the unit tests from the integration tests? I have all of the source code in src, the unit tests in test - should there be a 2nd test folder (such as test-integration) for integration test cases?
Since this is my first Spring project, I'm curious how others usually go about doing this sort of thing - and rather than re-invent the wheel I rather ask the rest of the community.

I can't speak to being a best practice, but here's what I've done in the past.
Unit tests:
Create unit tests for non-trivial beans (ie, most of your Spring related beans)
Use Mocks for injected services where practical (ie, most if not all the time).
Use a standard naming convention for these tests in the project test directory. Using Test or TestCase as a prefix or suffix to the classname seems to be widely practiced.
Integration Tests:
Create an AbstractIntegrationTestCase that sets up a Spring WebApplicationContext for use in intetgration test clases.
Use a naming convention for integration tests in the test directory. I've used IntTest or IntegrationTest as a prefix or suffix for these tests.
Set up three Ant test targets:
test-all (or whatever you want to name it): Run Unit and Integration Tests
test: Run Unit tests (just because test seems to be the most common usage for unit testing
test-integration: run the integration tests.
As noted, you can use the naming conventions that make sense for your project.
As to separating unit from integration tests into a separate directory, I don't think it matters as long as the developers and their tools can find and execute them easily.
As an example, the last Java project I worked on with Spring used exactly what is described above, with integration tests and unit tests living in the same test directory. Grails projects, on the other hand, explicitly separate unit and integration test directories under a general test directory.

A few isolated points:
Yes, it's a common approach to Spring testing - seperate unit tests and integration tests where the former doesn't load any Spring context.
For your unit tests, maybe consider mocking to ensure that your tests are focussed on one isolated module.
If you're tests are wiring in a ton of dependencies then they aren't really unit tests. They're integration tests where you are wiring of dependencies using new rather than dependency injection. A waste of time and duplicated effort when your production application uses Spring!
Basic integration tests to bring up your Spring contexts are useful.
The #required annotation may help you to ensure you catch required dependencies in your Spring wiring.
Maybe look into Maven which will give you explicit phases to bind your unit and integration tests on to. Maven is quite widely used in the Spring community.

A lot of the tedious double-book-keeping with spring goes away if you also switch to a purely annotated regime, where you annotate all your beans with #Component, #Controller, #Service and #Repository. Just add #Autowired to the attributes you need to get injected.
See section 3.11 of the spring reference manual. http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config
On a related note, we have been using the division Unit/Integratrion tests that KenG describe. In my most recent regime we have also introduced a third "class" of tests, "ComponentTests". These run with full spring wiring, but with wired stub implementations (using component-scan filters and annotations in spring).
The reason we did this was because for some of the "service" layer you end up with an horrendous amount of hand-coded wiring logic to manually wire up the bean, and sometimes ridiculous amounts of mock-objects. 100 lines of wiring for 5 lines of test is not uncommon. The component tests alleviate this problem.

Use the InitializingBean interface (implements a method "afterPropertiesSet") or specify an init-method for your beans. InitializingBean is typically easier because you don't need to remember to add the init method to your beans.
Use afterPropertiesSet to ensure everything is injected as non-null, if it is null, throw an Exception.

When I've created integration tests for web applications, I've put them in a separate directory. They are built using jUnit or TestNG and interact with the system under test using something like Selenium that hits the web pages as if they were users. The cycle would go like this: compile, run unit tests, build the web app, deploy it to a running server, execute the tests, undeploy the app, and report results. The idea is to test the whole system.

With regard to running unit tests separately from integration tests, I put all the latter into an integration-test directory and run them using IDE/Ant using an approach like this. Works for me.

the difference between unit test and integration test is , unit test does not necessarily load your context, you are focusing on the code which you have written - it works fails fast , that is with and without exceptions, by mocking any depends calls in it.
But in case of integration tests , you load context and perform end to end test like actual scenarios.

Related

Spilt Unit test and Integration test in Spring Boot

Need General Idea
What can qualify to be good unit test in spring boot?
Areas of discussion
Should we use #Autowired to call another class in unit test or everything use Mock.
#Mock Vs #MockBean
If we use #Mock and #Autowired together in Unit test class, can it be still a qualified to be unit test or becomes Integration test.
NOTE: Aware of #RunWith to make Integration and Remove Integration. My Question stress more about writing a good unit test in spring boot applications.
Thanks
It is generally a good practise to use #Mock for the dependencies while unit testing. you would not certainly need to load spring context to run your unit cases. When using Mocks, the test can be run independent of the spring context. #Autowired in unit testing slightly behaves more of an app integration testing where you are loading the spring context ,at least slice of it for the testing.
Consider a method like this PerformPayment(double amount, PaymentService service);
A unit test would be a test where you create a mock for the service argument.
An integration test would be a test where you use an actual external service so that you test if that service responds correctly to your input data.
If I go deeper in details
Unit tests are tests that the tested code is inside of the actual class. Other dependencies of this class are mocked or ignored, because the focus is to test the code inside the class.
Integration tests are tests that involve disk access, application service and/or frameworks from the target application. The integration tests run isolated from another external service.
I will give an example. You have a Spring application and you made a lot of unit tests to guarantee that the business logic is working properly. Perfect. But what kind of tests you have to guarantee:
Your application service can start
Your database entity is mapped correctly
You have all the necessary annotations working as expected
Your Filter is working properly
Your API is accepting some kind of data
Your main feature is really working in the basic scenario
Your database query is working as expected
Etc...
This can't be done with unit tests but you, as a developer, need to guarantee that all things are working too. This is the objective of integration tests.
The ideal scenario is the integration tests running independently from another external system that the application use in a production environment. You can accomplish that using Wiremock for Rest calls, a memory database like H2, mocking beans from some specific classes that call external systems, etc.
A little curiosity, Maven has a specific plugin for Integration Tests: the maven failsafe plugin, that executes test classes that the name ends with IT. Example: UserIT.java.
Read more by guru
https://martinfowler.com/bliki/IntegrationTest.html

What's the difference between #RunWith(MockitoJUnitRunner.class) and #RunWith(SpringJUnit4ClassRunner.class)? When to use it appropriately?

What's the difference between #RunWith(MockitoJUnitRunner.class) and #RunWith(SpringJUnit4ClassRunner.class)? When to use it appropriately?
MockitoJUnitRunner
specific for use with the Mockito test framework
the Mockito framework helps with mocking dependencies when you want to focus your tests on a single class and avoid invoking methods on dependencies (instead invokes a mock/dummy that is easily configured).
Above is what mockito is used for, but for more on this runner specifically - from the docs: "keeps tests clean and improves debugging experience". "Runner is completely optional - there are other ways you can get #Mock working". Source - https://static.javadoc.io/org.mockito/mockito-core/2.6.8/org/mockito/junit/MockitoJUnitRunner.html
SpringJunit4ClassRunner
specific for use with the spring framework
used for integration tests when it is required to load the spring context (create spring beans, perform dependency injection, etc).
In integration tests you may not do as much mocking of dependencies but you can do both in the same test.
Integration tests are useful when you would like to test loading the spring context or perhaps test from the service/high level all the way down to lower levels like data access with a single test.
In some cases you may want to use both - like an integration test where you would also like to mock some dependencies (perhaps they make remote calls). Unfortunately you can't use two #RunWiths but this is a good post about that - Multiple RunWith Statements in jUnit

spring - bake in component resolution

dependency injection might be good in theory - but in our world, it's just slow. Whenever we run tests or start our application - spring is spending 50 seconds in various calls to the PathMatchingResourcePatternResolver.
Is there any way to prevent that - to short circuit any spring discovery, and bake in a "when they ask for this bean, use this class" or something similar - so tat there is zero searching by spring?.
The main driver here is that creating a spring context takes literally 2 minutes - which of course is death to running unit tests by hand, where you want it to take more like 2ms.
You can have your test-applicationContest.xml to initialize only required dependencies for your test. I believe when you are running JUnit test, you are testing part of your application and not a whole which in turn require to initialize few things. You can have multiple test-applicationContext.xml files for different JUnit classes.
JUnit with Spring provide handful Annotations to load specific applicationContext file.
It sounds like what you are referring to as unit tests are actually integration tests that require a Spring context and associated container creation.
For unit tests just mock out injected dependencies using something like EasyMock.
For integration tests consider testing only the appropriate slices of your tier to speed up testing. See here:
https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
Keep in mind that by using the Spring testing support you get a cached Spring context, so if you are writing integration tests, you generally only need to start the container once as Spring will cache it after that.

spring integration test cleanup for conflicting configuration contexts

I'm working on an application where we use integration tests intensively since a core framework we are using operates on the database.
I have test classes using configuration context class such as this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ConfigA.class)
public A_Test(){
}
The majority of tests are using same context like above. We have over 200+ such tests. But recently we needed some additional configuration for some use cases as well, like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ConfigA.class, ConfigB.class})
public B_Test(){
}
Problem is now when we execute all tests with maven, or IDE runners , loaded cache for ConfigA no longer works. Spring tries to recreate a context for ConfigA which fails because we have H2 DB already configured and Spring tries to create schemas, tables which fails to do so.
To overcome we started to use #DirtiesContext on all tests. Result is over 1H build time, which reduces the developer productivity significantly.
Question: is it possible to clear context for tests like B_Test only? #DirtiesContext(ClassMode=AFTER_CLASS) doesn't help because order of the tests are not guaranteed(and really we don't want to go that way). It fails when type of B_Test tests are last to run. Same for #DirtiesContext(ClassMode=BEFORE_CLASS) visa versa
Is it possible to simulate #DirtiesContext(ClassMode=AFTER_CLASS) and #DirtiesContext(ClassMode=BEFORE_CLASS) at the same time on a bunch of tests?
Or is there any other way to solve in general this problem?
What we tried so far:
Junit Suites : didn't help anything with spring context
ContextHierarchies : didn't help with the case that B_Type tests also dirties the context
Test Ordering: well nobody is really happy about refactoring all the tests to make it work magically
How about using both #DirtiesContext(ClassMode=AFTER_CLASS) and #DirtiesContext(MethodMode=BEFORE_METHOD)?
When you do that, Spring will reload context ConfigA.class and ConfigB.class just before invoking test methods annotated with #DirtiesContext(MethodMode=BEFORE_METHOD).
And then, after all tests of B_Test finished, Spring shutdowns the contexts (and next test class with SpringJUnit4ClassRunner will load its context).
This is essentially a duplicate of Make Spring Boot Recreate Test Databases.
In summary, you likely only need to ensure that you are using unique names for each embedded database that is created using your ConfigA class.
Please read my comments here for details: https://stackoverflow.com/a/28867247/388980
Also, see the comments in SPR-8849 for further details.
Regards,
Sam (author of the Spring TestContext Framework)

Integration tests with JUnit and web MVC

I am working on a Spring MVC application.
Unit tests are already writen (nice 85% code coverage and lots of assertions :)
Now I need to write integration tests. I already have a look at stackoverflow still I do have some questions
Right now I am using a standard maven structure with main and test directories, do you recommend to create another directory called integration-tests and write the unit tests there? If so, why?
Another alternative would be to write the integration tests in the "tests" directory, mixing unit and integration tests, and then using maven to run one or the other (maybe using different file suffixes depending on the test type)
In any case, the way I am planning to run the integration tests is essentially to (almost) reuse the unit tests of the controllers WITHOUT injecting mocks, that means that my current stack (Controller-Service-DAO) will contain no mocks (of course, in the unit tests, it does), by removing the mocks I will access to the real resources (Database and so), is this a good approach?
UPDATE: Just to clarify, the project has not JSP or any HTML-related views, output is XML which can be easily validated with XSDs
I am assuming that you have used JUNIT to create your unit tests to achieve the impressive 85% code coverage. Please notice that JUNIT is designed for unit testing only (thus the name JUNIT). Unit testing is done while the code is running in the development environment.
Integration testing can only be performed once the target code has been deployed in the target integration environment.
You have mentioned that you application isn't a web application. Is it SOAP/Rest Web Service? If so, you can use Soap UI [http://www.soapui.org/], to create and save automated regression/integration tests.

Categories