Spring Boot always loads full components in the container when I try to run a junit test everytime.It takes more than 2 minutes to run,and that wastes me a lot of time.So,how can I avoid this?
You could and should avoid using Spring Dependency Injection in unit test, otherwise is more like an integration test then a real unit test.
You could manually instantiate your under test classes (via constructor) and mock their dependencies.
A cool library like mockito can help you with mocks.
Related
How to write a proper test case for controller, service and Dao in spring boot using junit 5 with clear explanation
Spring Boot has a concept of test slices. This type of test configuration will setup only a part of your application and thus making tests:
less likely to break on not-related change,
faster in comparison to configuring all application services (using #SpringBootTest annotation).
For example #JsonTest slice will configure ObjectMapper (and some test utilities for JSON) in the same way as it would happen on production.
Anyway, to your mentioned types:
DAO - use #DataJpaTest slice - it will configure Hibernate with in-memory database and load all your entities and repositories.
Controllers - use #WebMvcTest(YourController.class) slice - it will load only configuration for Spring MVC, advices and your controller. You will be responsible to deal with dependencies of that controller.
Services - pretty much depends on what is your service doing. I prefer using slices also for services depending on Spring-configured beans but your test can also be a very simple standard [j]unit test with all dependencies mocked away. - Depending on the compromise you want to make.
This does not change with the fifth version of junit. The only difference is that you no longer need to annotate your tests with #RunWith(SpringRunner.class).
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.
I have a Spring boot application initially setting on MySQL, so far so good. However now I am trying to create more unit test for JPA / DAO layer with H2 database.
I see several online demo that in Spring it is common practice to have an applicationContext-test for testing context setting.
Is it still good practice in Spring boot 1.4?
#SpringBootApplication(scanBasePackages = {...})
public class ApplicationTest extends SpringBootServeltIntializer{
....
}
As currently there is no separate xml file holding context for testing, is above looks like a good solution? And also is there performance impact that when the application starts all context for testing are also need to loaded in memory?
Also does that mean I need to create an application.properties in test sources? Spring boot has a lot of implicit process behind, but I cannot find much texts explain about the DAO layer setting for test in Spring Boot, so any guideline is appreciated.
My preference is to not use Spring for JUnit testing at all.
JUnit tests, by definition, should be about unit testing individual classes. Spring is a DI engine for satisfying dependencies. Using the real dependencies breaks the idea of a unit test; for those I manually inject mocks.
I do that to restrict the tests to individual classes. I find that creating the Spring factory and all the application beans takes a long time. I don't want to pay that price when I have a lot of unit tests. Keeping Spring out of the mix makes my tests run faster.
When I run an integration test of camel route processing as a standalone test, it passes. When I run all the tests, it fails with assertion errors of expected values. When I add #DirtiesContext to the abstract test class, they all pass ok.
I checked the docs and beyond the paragraph below, they do not say why #DirtiesContext is actually needed, and what goes wrong when not used.
Notice that we use #DirtiesContext on the test methods to force Spring Testing to automatically reload the CamelContext after each test method - this ensures that the tests don't clash with each other (e.g. one test method sending to an endpoint that is then reused in another test method).
The thing is that creating spring context again and again is quite time consuming. All our other non-camel integration tests pass without #DirtiesContext (using #Transactional) and we would like to continue with that.
Is there a way to put camel to pristine state without recreating the whole spring context so the test's MockEndpoints work as expected?
I want to accomplish this - Run a background process (a Solr instance actually) that all the tests in my JUnit Suite will use.
To do this - Created a JUnit class annotated with #RunWith(Suites.class). And added a ClassRule on the Suite to start the server and stop it. Individual tests in the suite were annotated with #SpringApplicationConfiguration and #RunWith(SpringJunit4ClassRunner.class). And I also require access to some of the Beans in the Suite itself (like a spring managed settings bean). What's the best way to do this. What I tried.
Annotated individual tests with #SpringApplicationConfiguration
Had the Suite create an ApplicationContext via
SpringApplication.run and access any bean that it wants (a spring
managed Settings bean for example and use it one of ClassRules of
this Suite).
What I observed is that the ApplicationContext context gets created everytime, One for the Suite because I called SpringApplication.run and one for every test. I obviously want to avoid this and caching of the ApplicationContext between test runs also does not seem to work in this case.
So what are the best practices to handle this case.
Any suggestions/recommendations will be highly appreciated.
This is a long forgotten question, but it shown up on my google search, so I am assuming it still somehow relevant :)
I was going down on the same path but I hit so many road blocks that I decided to change my approach:
Create a JUnit runner as described here.
Change #1 to inherit SpringJUnit4ClassRunner here is an example.
Finally, annotate your Test classes with #RunWith(MyCustomRunner.class)
There you go. You can add whatever logic you need inside your runner.