advice on integration testing my dao layer - java

I have a spring mvc application, and I am using jdbctemplate for my database Dao objects.
How should I go about integration testing?
Where should I put my integration testing files, is this layout correct:
/src/main/test/integration/...
or
/src/main/integration/...
How will this work, for my test cases, I should have a base class or some code in my setup that will run once before starting my testing where it loads the spring configuration/application context.
How should I do this?

If You are using Maven, tests should go to src/test/java. If You're not, it seems like a reasonable place anyway.
To set up spring context You should use #RunWith(SpringJUnit4ClassRunner.class) together with #ContextConfiguration, no initialization code necessary.

Related

void method in Java Integration Tests

I have some experience with Unit Tests in Java and now started to write Integration Tests. However, I have some troubles understanding the Integration Test and writing test. Here are some issues that I would like to be clarified:
1. In my Java (based on Spring Boot) project, should I write Integration Test for Controllers or may it be also ok to write Integration Test for Services also (because there are some methods that are not called from Controller).
2. How can I test a void in service by Integration Test? I have not found a proper example on the web and I thought there is no need or way to test void via Integration Test. Any clarification pls?
OK, so here are the best practices in the area.
should I write Integration Test for Controllers or may it be also ok to write Integration Test for Services also
Most of the integration tests run for services. This is because each service can support several controllers and multiple interfaces in general. For controllers you do just the happy path tests on an integration level.
Any tests for controller specific exceptions go into the controller unit test, where you mock out the service.
How can I test a void in service by Integration Test?
You check its side effect. For example, you use a repository class to see if the relevant data has been persisted to the database.
Does this solve your problem ? Let me know in the comments.

Use one spring boot context through all SpringBootTests

I want to be able to cache application context through different classes with tests using junit.
Test classes are declared this way:
#SpringBootTest
#RunWith(SpringRunner.class)
public class SomeIntegrationTest {
}
I saw this question Reuse spring application context across junit test classes but in this case I don't use any xml and I want to start context completely, not just few beans from it, so #SpringBootTest is more suitable than #ContextConfiguration, if I got it right.
Ruslan, so your question is on how to reuse the Spring Boot Context for a JUnit Suite, right?
Then, it's almost out-of-the-box provided, you just need to annotate each unit test with the #SpringBootTest annotation.
Also make sure that your main #SpringBootApplication class is loading all the necessary #Configuration classes, this process will be automatically done if the #SpringBootApplication is on the root package above all configuration class and with the inherited #ComponentScan will load up all of them.
From the Spring Boot Testing documentation:
Spring Boot provides a #SpringBootTest annotation which can be used as an alternative to the standard spring-test #ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests via SpringApplication.
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
From the Spring Testing documentation:
By default, once loaded, the configured ApplicationContext is reused for each test. Thus the setup cost is incurred only once per test suite, and subsequent test execution is much faster. In this context, the term test suite means all tests run in the same JVM
Check this urls:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management-caching
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testing-ctx-management
Main Takeaways:
Annotate each unit test with #SpringBootTest
Load all beans and necessary configuration classes in your main #SpringBootApplication class
IMPORTANT: Run a JUnit Suite, not a single JUnit test. Execute all tests as a group within your IDE.
#SpringBootTest provides ApplicatonContext caching and sharing in Junit Test Cases naturally.
While some cases may be exceptions. For example, using #MockBean or #SpyBean in a test class will cause the ApplicatonContext cache failure.
While Spring’s test framework caches application contexts between
tests and reuses a context for tests sharing the same configuration,
the use of #MockBean or #SpyBean influences the cache key, which will
most likely increase the number of contexts.
https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/html/boot-features-testing.html
And this kind of issue is still not solved in the latest SpringBoot release.
https://github.com/spring-projects/spring-boot/issues/21099
https://github.com/spring-projects/spring-boot/issues/7174

Spring context dirty after each integration test

I recently started as a freelancer on my current project. One of the thing I threw myself on, was the failing Jenkins build (it was failing starting from April 8th, a week before I started here).
Generally speaking, you could see a buttload of DI issues in the log. First thing I did, was get all tests to work in the same way, starting from the same application context.
They also implemented their own "mocking" thing, which seemed to fail to work correctly. After a discussion with the lead dev, I suggested to start using Springockito. (for a certain module, they needed mocking for their integration testing -- legacy reasons, which can't be changed)
Anyway, stuff started failing badly after that. A lot of beans which were mocked in the test, simply weren't mocked, or weren't found or whatever. Typically, it would fail on the loading of the application context, stating that one or another bean was missing.
I tried different stuff and different approaches, but in the end, only the thing I most feared would work: add #DirtiesContext to every single test. Now, the maven build is starting to turn green again, tests start doing what they are supposed to do. But I am reloading the Spring context each and every time, which takes time - which is all relative, since the context is loaded in about 1 - 2 seconds.
A side note to this story is that they've upgraded to Hibernate 4, and thus to Spring 3.2. Previously, they were using an older version of Spring 3. All tests were working back then, and the #DirtiesContext thing was not necessary.
Now, what worries me the most, is that I can't immediately think of an explanation for this weird behaviour. It almost seems that Springs context is dirtied, simply by launching a test which uses #Autowired beans. Not all tests are using Mocks, so it can't be that.
Does this sound familiar to anyone? Has anyone had the same experiences with integration testing with (the latest version of) Spring?
On Stackoverflow, I've found this ticket: How can a test 'dirty' a spring application context?
It seems to pretty much sum up the behaviour I'm seeing, but the point is that we're autowiring services/repositories/..., and that we don't have any setters on those classes whatsoever.
Any thoughts?
Thanks!
To answer my own question, the secret was in the Spring version. We were using Spring 3.1.3, whereas I presumed they were using Spring 3.2 (they were constantly speaking about a recent upgrade of the Spring version).
The explanation was here, a blog post I stumbled over in my hunt to get it fixed: Spring Framework 3.2 RC1: New Testing Features
And a copy paste of the relevant piece:
The use of generic factory methods in Spring configuration is by no means specific to testing, but generic factory methods such as EasyMock.createMock(MyService.class) or Mockito.mock(MyService.class) are often used to create dynamic mocks for Spring beans in a test application context. For example, prior to Spring Framework 3.2 the following configuration could fail to autowire the OrderRepository into the OrderService. The reason is that, depending on the order in which beans are initialized in the application context, Spring would potentially infer the type of the orderRepository bean to be java.lang.Object instead of com.example.repository.OrderRepository.
So, how did I solve this problem? Well, I did the following steps:
create a new maven module
filter out the tests which needed mocking. All the non-mocked test would run normallly in a Spring build, in a separate Failsafe run (I created a base-package "clean", and sorted them out like that)
Put all the mocked tests in a base package called "mocked", and make an additional run in Failsafe for the mocked tests.
Each mocked test is using Springockito, to create the mocks. I'm also using the Springockito annotations, to easily do a #ReplaceWithMock in place. Every mocked test is then annotated with #DirtiesContext, so the context is dirtied after each test, and the Spring context is reintroduced with each test.
The only reasonable explanation that I could give, is that the context is effectively being dirtied, because there is a framework (Springockito) which is taking over the management of the Spring beans from the Spring framework. I don't know if that's correct, but it's the best explanation I could come up with. That, in fact, is the definition of a dirty context, which is why we need to flag it as dirty.
Using this strategy, I got the build up and running again, and all tests are running ok. It's not perfect, but it's working, and it's consistent.

Using Junit with spring

I'm trying to set up a junit with spring and I'm trying to use spring's dependency injection to populate the test class. I'm wondering if this is something I should even be attempting? I think what I'm seeing is spring is instantiating the test class and performing the DI but then JUnit is creating it's own instance that hasn't had DI performed and the test is failing. I'm using JUnit 4.x and spring 3.1.1.
You can use spring to inject dependencies into your tests, thus making it an integration test. Annotate like this
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#ContextConfiguration(locations = "/applicationContext-TEST.xml")
public class MyTest {}
But it can be preferable to just test your spring managed classes as pojo's and use mock objects where appropriate.
For example a lot of controller methods have a Model injected at runtime by Spring. However to unit test them I just pass in an HashMap instance. And my service layer classes I can pass in a mocked dao, which is easy because I designed to an interface and use setter injection...
With jUnit, each test should be isolated with no dependency outside of test coverage. There are several test frameworks available that provide mock bean instantiation in Spring.
There is an excellent Martin Fowler article on Stubs and Mocks to begin with.
Mockito in combination with PowerMock, can help you test spring components, services and controllers.
Mockito Intro: https://code.google.com/p/mockito/
PowerMock Intro: http://code.google.com/p/powermock/
I understand this will take up time to research, learn and implement, but this is very beneficial for writing jUnit tests with Dependency Injected beans.

How to add a spring application context to the java runtime to be used for integration tests within eclipse?

I am trying to add a test spring application context to the java runtime, so that my beans can be wired properly for my integration tests.
Never mind, I solved it. I just had to add the folder containing my test application context as a source folder, and VOILA! I am pretty sure there are better ways to do it.
Spring supplies some tools that can help with such testing. See Spring TestContext Framework.

Categories

Resources