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)
Related
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
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.
I have a set of tests based which need a spring context.
For fast test execution I want to make sure that the Spring context is initialized just once, then all the tests should be run against this context, then it should shut down.
I already tried the following approaches:
Use #RunWith(SpringJUnit4ClassRunner.class) and #ContextConfiguration(MyAnnotatedConfig.class) to initialize the spring context
Use a #RunWith(SpringJUnit4ClassRunner.class) and #TestExecutionListeners({MyTestExecutionListener.class}) with a handwritten test execution listener that initializes the spring context and injects it into the concrete test classes
Use a #BeforeClass listener in a base class and a static field to store the spring context, and a #AfterClass for shutdown
With all three approaches, the spring context seems to be initialized more than once, which takes a lot of time. It seems that JUnit unloads classes while running tests, so that the content of static fields is sometimes lost.
Is there a way to make sure the spring context is initialized only once?
For fast test execution I want to make sure that the Spring context is
initialized just once, then all the tests should be run against this
context, then it should shut down.
I hate to ask the obvious, but...
Have you read the Testing chapter of the Spring Reference Manual?
Specifically, these sections explain what's going on:
Context management and caching
Context caching
Soooo, the TestContext framework certainly supports caching across tests within a test suite, and I should know, because I wrote it. ;)
Now as for why caching is not working for you, I can only assume that you have configured your build framework to fork for each test (or that you are running tests individually and manually within your IDE). Here's an excerpt from the last link above that might help you out:
Test suites and forked processes
The Spring TestContext framework stores application contexts in a
static cache. This means that the context is literally stored in a
static variable. In other words, if tests execute in separate
processes the static cache will be cleared between each test
execution, and this will effectively disable the caching mechanism.
To benefit from the caching mechanism, all tests must run within the
same process or test suite. This can be achieved by executing all
tests as a group within an IDE. Similarly, when executing tests with a
build framework such as Ant, Maven, or Gradle it is important to make
sure that the build framework does not fork between tests. For
example, if the forkMode for the Maven Surefire plug-in is set to
always or pertest, the TestContext framework will not be able to cache
application contexts between test classes and the build process will
run significantly slower as a result.
If you still experience issues after taking the above into consideration, please consider submitting a project that demonstrates your problem.
Cheers,
Sam
I'm hitting a strange error related to unit-testing in my Java application.
During unit-testing I use in-memory HSQLDB pre-filled with custom data (via an Insert script triggered automatically) and Hibernate as ORM to access it.
Problem is following, if I start the unit-test on a single class (i.e.: TestDummyClass.java) the db is recreated (from the original insert script) after each method test.
If I launch the unit-test on the whole project (src/test) which contains multiple test classes, the DB is initialize on the beginning for each test-class and not on each test-method.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:com/wizche/app-context-test.xml" })
public class TestDummyClass {
.....
}
This means for example that if in TestDummyClass I change the DB (i.e. creating a new object) in some test-method the new object will stay there for the following test-methods in the same class. Therefore the asserts should differs if I start it on the project or on the single class!
Can someone explain me why is this happening? How can I decide when to restore the clean-db?
NB: I'm using SpringJUnit4ClassRunner with a custom context configuration for the whole test-project (in which there is no parameter related to unit-testing).
NB2: I start JUnit direct in SpringEclipse
The reason is that if you use SpringJUnit4ClassRunner, it tends to cache the application context if you use the exact same location for multiple tests - this caching is across a suite so if you execute all the tests in your project in a single suite, and use the same application context location across multiple tests, it is likely that you will get a cached context - and if you modify beans in a context, that will reflect for other tests too.
A fix is to add #DirtiesContext annotation to the test class or test method
Eclipse runs unit tests in a single JVM so theoretically even running on a single class you should see the same behaviour as running from src/test. I'm thinking running all your tests your more likely to see a problem. What makes you sure the DB is recreated after each method when you a single class test - just want to make sure this is accurate?
A similar question has been raised before check this link out. It will be helpful.
I would suggest using DBUnit to assist with building & tearing down your database. It provides methods to do this on the fly and interacts with all the main database providers.
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.