Should I always start Spring context during integration tests? (I mean using #SpringBootTest annotation)
Currently I'm writing integration test that involves a few classes and in order to make it faster I create object graph by hand (i.e. I don't start Spring IoC container). So currently my integration test (written in Spock) looks like this:
class UserConverterIT extends Specification {
UserConverter converter = new UserConverter(new UserDtoFactory(new UserGroupPolicy()))
def 'should ...'() {
when:
converter.convert(...)
then:
...
}
}
Alternatively I could add #SpringBootTest annotation, put #Autowire above UserConverter field and all dependecies would be injected automatically. However, the first approach is much faster. Is there something wrong in this approach?
As you said the #Autowired annotation would inject all dependencies and load the whole context automatically. Your approach is also working but its really fragile!
How can you guarantee that in your tests you never need some beans which you didn't manually new them?
Also there is another important thing. When you let the spring to inject the dependencies, if there is a problem on declaring the beans, problem would show on test phase, but in your approach they won't identify.
Also you might sometimes #Autowired an interface which told spring to get the implementation on runtime. For example you have a parent module which has an interface that implements in the child module. When you want to write a test case in parent you don't have access to child implementation to make new of that.
Related
I know that there are questions similar to this one, but none of them have helped me. I'm following along this tutorial, and the part I can't wrap my mind around is:
#SpringBootApplication
public class Application {
private static final Logger log =
LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public CommandLineRunner demo(CustomerRepository repository) {
return (args) -> {
// save a couple of customers
...
// more lines, etc...
What I don't understand is where the repository passed into demo comes from. I know that the Autowired annotation can do something like that, but it isn't used at all here.
The more specific reason I ask is because I'm trying to adapt what they do here to an application I'm working on. I have a class, separate from all of the persistence/repository stuff, and I want to call repository methods like save and findAll. The issue is that the repository is an interface, so I can't instantiate an object of it to call the methods. So do I have to make a new class that implements the interface and create an object of that? Or is there an easier way using annotations?
When creating a #Bean, adding the repository in the parameters of the bean is enough to wire the repos in your bean. This works pretty much like adding #Autowired annotation inside a class that is annotated as #Component or something similar.
Spring works mostly with interface, since that is simplier to wire vs wiring concrete classes.
Can you try #Repository before the declaration of class? Worked for me in a Spring MVC structure.
#Repository
public class EntityDAOImpl implements EntityDAO{
...
}
The thing to wrap your head around is a Spring Boot application at startup time aims to resolve its dependancy tree. This means discovering and instantiating Beans that the application defines, and those are classes annotated with #Service, #Repository, etc.
This means the default constructor (or the one marked with #Autowire) of all beans is invoked, and after all beans have been constructed the application starts to run.
Where the #Bean annotation comes into play is if you have a bean which does not know the values of it's constructor parameters at compile time (e.g. if you want to wire in a "started at" timestamp): then you would define a class with an #Configuration annotation on it, and expose an #Bean method in it, which would return your bean and have parameters that are the beans dependencies. In it you would invoke the beans constructor and return the bean.
Now, if you want a certain method of some class to be invoked after the application is resolved, you can implement the CommandLineRunner interface, or you can annotate a method with #PostConstruct.
Some useful links / references:
https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/creating-bean-definitions.html
https://www.baeldung.com/spring-inject-prototype-bean-into-singleton
Running code after Spring Boot starts
Execute method on startup in Spring
I am trying to unit test my Service classes that looks similiar to this:
#Service
public class SomeQueryService {
private final SomeRepository repository;
public SomeQueryService(SomeRepository repository) {
this.repository = repository;
}
public void doSomething() {
// code doing some magic
}
}
SomeRepository is simple repository interface extending JpaRepository interface.
What I want to do is unit test this service to verify whether it is working properly.
I do not want to use mockito to mock repository behaviour instead, I want to have some in-memory implementation (on list or map) that will imitate database behaviour.
Does Spring provide such fake implementations?
I want to avoid making Stub Implementation of such repository by myself as I will be using such tests in many other places.
RealLifeDeveloper has created an MIT-licensed helper-class to do just what you want: implement the repository-interface with a plain-old-java-object that just wraps a Collection, and called it "InMemoryJpaRepository". You will still have to implement some logic yourself1, though it should be easy if your queries are not too complicated.
An article explaining how to do this with example: https://reallifedeveloper.com/creating-in-memory-versions-of-spring-data-jpa-repositories-for-testing/
The repository (which includes other stuff, too) is on github: https://github.com/reallifedeveloper/rld-build-tool
The specific helper-files for creating the inmemory-db are found at https://github.com/reallifedeveloper/rld-build-tools/tree/master/src/main/java/com/reallifedeveloper/tools/test/database/inmemory if you dont want the whole repo.
1 The rule "Don't put logic in tests" exists for a reason and is clearly violated here. However, the well-known and widely-used alternatives mentioned by the other answers, H2-testing and extensive mocking, have their drawbacks too.
The type of testing you are referring to is called "Integration Testing" or "End to end testing" since it tests the whole application or a big chunk of it compared to unit tests that test only one method.
https://www.guru99.com/unit-test-vs-integration-test.html
You should not unit test your repositories, since they are already well tested by the spring team.
Solution:
You can create a test that starts the whole spring container using Spring Boot:
Just create a class in your test folder and annotate it with:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTestClass {
#Test
public void test() {
}
}
You can then configure an embedded database using H2 so that your test does not use the production database, just follow the Spring Boot Database Initialization doc.
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
PS. You can also create a specific profile using the following annotation on your test class:
#ActiveProfiles("test")
No Spring Data does not provide a fake implementation of that interface.
You'll have to create it on your own or use a mocking framework like Mockito.
Spring Boot 1.4 offers some fantastic testing improvements. One is the #DataJpaTest annotation where it wires up just the parts needed for JPA testing. What would the equivalent look like for just wiring up the parts needed for JdbcTemplate tests?
I'm fine constructing my own composite annotation that mimics the #DataJpaTest one.
Good question. Ironically enough, that one was raised during the testing talk yesterday at SpringOne Platform. Let's see what it takes to implement such dedicated test annotation.
TL;DR check the code on github
First of all you need to create the annotation. This annotation reuses some bits from the spring-boot-test-autoconfigure module. You may want to auto-configure an in-memory database (like DataJpaTest does). You also want to make sure that caching is configured and disabled by default (in case you have #EnableCaching on your Spring Boot application). You also want that all your tests are #Transactional by default so you should add that.
Next, you want that slicing effectively kicks in. All you need at this point is a DataSource, a JdbcTemplate, database migrations (flyway/liquibase) and a transaction manager to process #Transactional. To avoid the other auto-configurations to kick in you should add the following:
#OverrideAutoConfiguration(enabled = false)
Then, you want to explicitly enable the auto-configurations above. In order to do so, you add #ImportAutoConfiguration and you add the following content in META-INF/spring.factories
# AutoConfigureDataJpa auto-configuration imports
com.example.test.autoconfigure.jdbc.DataJdbcTest=\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
The key in spring.factories should match the FQN of your annotation. Whenever Spring Boot finds #ImportAutoConfiguration with no extra attributes, it will look for a key matching the annotation type in spring.factories.
Next up you want to be able to include additional components (component scan) with a filter. In order to do that, you can add #TypeExcludeFilters(DataJdbcTypeExcludeFilter.class) where DataJdbcTypeExcludeFilter is pretty much the same thing as DataJpaTypeExcludeFilter (so we might want to extract a common class for that).
Once you've done that, you only need to add your annotation and your JdbcTemplate is auto-configured for you
#RunWith(SpringRunner.class)
#DataJdbcTest
public class DataJdbcSampleTests {
#Autowired
private JdbcTemplate jdbcTemplate;
...
}
I think the option will be #JdbcTest, you could found further info on doc.
The typical exmaple is:
#ContextConfiguration(locations = {
"classpath:someITTest-mock.xml",
"classpath:someITTest-context.xml",
"classpath:commons.xml"})
#Transactional
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeITTest {
//...
}
I am finding the way to recreate mocks during tests. The obvious way to use DirtiesContext
But it results to recreate all contexts, when only mock context should be recreated.
Here is several notes:
Context with mock seems to be loaded first, because other depended
context do not have such beans.
When mocks are created and injected
into depended context, it is probably impossible to cache/reuse
depended context.
So is there a way to recreate spring context where mocks are created, but leave other context cached?
Update: If you are using Spring Boot 1.4, you can use its first-class support for mocking beans via #MockBean.
So is there a way to recreate spring context where mocks are created, but leave other context cached?
No, that is not possible.
In your example, there is in fact only one single ApplicationContext which is loaded from all three XML configuration files. If you create a hierarchy using #ContextHierarchy, there would be multiple contexts; however, it is impossible to reload only a parent context (see other comments I've posted in this thread).
However, one common approach that people take in such scenarios is to explicitly reset the mocks in question.
There are basically two ways to achieve this.
If the mocks are injected into the integration test (e.g., via #Autowired) you can simply reset the mocks in an after method (e.g., in an #After method in JUnit 4).
Otherwise, you can implement and register a custom TestExecutionListener that retrieves the mocked beans from the ApplicationContext and resets them (e.g., in the afterTestMethod() method).
Regards,
Sam (author of the Spring TestContext Framework)
You may use #ContextHierarchy and #DirtiesContext:
#ContextHierarchy({
#ContextConfiguration(name="parent",
locations = {"classpath:someITTest-context.xml", "classpath:commons.xml"}),
#ContextConfiguration(name="child",
locations = "classpath:someITTest-mock.xml")
})
#DirtiesContext(hierarchyMode = CURRENT_LEVEL)
With this, only the child context should be reloaded, see Testing.
I want to test my spring mvc controller.
The controller has a service:
#Autowired
UserService userService
And my user service depends on (autowired) my UserDao and some other services like mongoDb etc.
Now I want the business logic to be tested in my UserService, but ofcourse I want to mock the responses from my UserDao and Mongodb etc.
How do I setup my unit test correctly?
Can I re-use the spring container's xml file that has all my beans etc. or do I create a new one? (I'm assuming I have to get the spring container involved here)
Looking for some guidance on this, any tutorials would be greatly appreciated.
Update
What I find strange is that for my spring controller (that doesn't implement from Controller) I was able to access my private varialbe to manually set my service, i.e:
#Controller
public class UserController {
#Autowired
UserService userService;
}
And in my unit test I could do:
UserController controller = new UserController();
controller.userService = ....
But for my UserService, which has UserDao autowired, I can't access the userDao property:
UserService userService = new UserServiceImpl();
userService.userDao = .... // not available
It makes sense since it is private, but how is it working for my controller?
Spring framework has very interesting features for testing. You can take a look at Spring reference guide. It can provide DI even in your JUnit test class.
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "/applicationContext.xml" and "/applicationContext-test.xml"
// in the root of the classpath
#ContextConfiguration(locations={"/applicationContext.xml", "/applicationContext-test.xml"})
public class MyTest {
// class body...
}
Briefly, you can use your own applicationContext.xml or even define a new one just for testing. I personally use a different one since I define another dataSource dedicated for testing purposes.
What I find strange is that for my spring controller (that doesn't implement from Controller) I was able to access my private varialbe to manually set my service, i.e:
This is easy: the varible in not private.
It has the default visibility ("package private"). This mean you can access them from all classes of the same package.
So if you have a common structure, then the controller and the controller test case are in the same package. Therefore you can modify the ("package private") controller fields. But the controller test case and the service are not in the same packaged, so you can not access the ("package private") service fields.
Can I re-use the spring container's xml file that has all my beans
etc. or do I create a new one? (I'm assuming I have to get the spring
container involved here)
I would advice against creating new xml file. You would end up duplicating lot of stuff and its going to be hard to maintain. There would be proliferation of config files. You put the config required for tests in a different xml and that should not even be deployed to your production box. So far as using the config for your beans, you can employ the mechanism as suggested by #Trein.
For testing spring contrller in general, you may also find this SO Thread useful.
Hope that helps.