31

A simple question that might have an advanced answer.

The Question: My question is, is there a way to instantiate only the classes, in your application context, needed for that specific JUnit test ?

The Reason: My application context is getting quite big. I also do a lot of integration tests so you I guess you would understand when I say that every time I run a test all the classes in my application context get instantiated and this takes time.

The Example:

Say class Foo inject only bar

public class Foo {

@Inject
Bar bar;

@Test
public void testrunSomeMethod() throws RegisterFault {
    bar.runSomeMethod();
}

but the application context has beans foobar and bar. I know this is not a vaild application context but rest assure all my code works.

<beans>
     <bean id="foobar" class="some.package.FooBar"/>
     <bean id="bar" class="some.package.Bar"/>
<beans>

So how do I tell spring to only instantiate Bar and ignore FooBar for the test class foo.

Thank you.

SandMan
  • 555
  • 2
  • 6
  • 15
  • 1
    Have you tried to create context one time per all tests? Or is it not an option? http://stackoverflow.com/questions/8501975/reuse-spring-application-context-across-junit-test-classes – Nickname0222022022 Aug 18 '16 at 09:29
  • I have considered it but I have a lot of tests so to create a application context for each test will take time. I was hoping to just tell spring what I need from said application context – SandMan Aug 18 '16 at 10:26
  • I think we have misunderstanding. I meant, create one time. So you do not create context for each test. You create context and then launch tests on this created context before. If that's not the option, cause you're making some changes in beans, and you need to reset them, then VinayVeluri answer is good. But you need to create seperate xml files, which can be a little annoying, looking at that, that you have like 100 files of context. If you want seperate context per each test o0 Or mix solutions ;) – Nickname0222022022 Aug 18 '16 at 10:29
  • Oooh ok I understand your answer to the question. I thought it was the same as Vinay Veluri. See the problem with this solution is. It works great if you run all the tests at the same time and that is actually how I am busy testing but if you just want to run one test multiple times ( say you are busy testing code something does not work you make changes and then test again ) it takes time. I am trying to eliminate the time it takes – SandMan Aug 18 '16 at 11:39
  • Two solutions at the same time, would resolve your problem I guess :P But you can't merge them. SO when full test going, create once, but when testing separate- create minimum needed context. I don't know if it's possible, like smart test runner, would know what context it shall created. Or maybe change your approach to testing? Integration tests imho are supposed to run all at once. Like in Jenkins or so. Cause they're long from their definitions. Run one by one, should be clean Unit test. And unit tests are like less than second. So I'd go for mocks - Mockito. As always. But that's offtop. – Nickname0222022022 Aug 18 '16 at 11:45
  • Alright, might write something in my own time to accomplish what I want. I am actually surprised it does not exist. Sure unit testing works fine but I like to see that it works like it suppose to work before I push my work to dev. Anyway have a good one – SandMan Aug 18 '16 at 12:05
  • Also if you write your solution as an answer I will accept it – SandMan Aug 18 '16 at 12:06
  • Thanks for that :) But that did not solve your problem unfortunately. And it's a workaround. Maybe someone has an answer, I would not want to block that for ya. And I'm not saying, that I'm 100% sure it does not exists. Just never found solution for that. And all sources just confirms that. Would be usefull in many projects before, where I was not worked alone, and no one ever found solution, in team we just created one special smaller context special for tests with some spring mocks and we created context one time before all test. So as said before :) – Nickname0222022022 Aug 18 '16 at 12:12
  • Sure I understand but I searched my self and I almost wasted a day trying to find a solution. So up you go. Also it is a solution to my problem just not the one I wanted – SandMan Aug 18 '16 at 12:27
  • Hi, how to write the `test-context-case1.xml` to automatically include all files inside some packages (e.g. com.company.project.module_one)? I know how to do that using `@ComponentScan`, but it seems that this does not work here... Thanks! – ch271828n Jun 27 '20 at 01:32

3 Answers3

9

Consider adding default-lazy-init="true" to your spring context xml beans tag (or add lazy-init="true" to those specific beans that take a long time starting up). This will ensure that only those beans are created that called with applicationContext.getBean(class-or-bean-name) or injected via @Autowired / @Inject into your tests. (Some other types of beans like @Scheduled beans will be created nevertheless but you need to check if that's a problem or not)

(if you use spring Java configuration, add @Lazy to the config files)

Caveat - If there is a bean that is not initialized explicitly with applicationContext.getBean() or injected as a dependency used by the bean obtained by using applicationContext.getBean(), then that bean will NO LONGER be constructed or initialized. Depending upon your application, that can cause things to fail OR not. Maybe you can selectively mark those beans as lazy-init="false"

Deepak
  • 3,648
  • 1
  • 22
  • 17
7

Yes, we can do that, using context per test case. Prepare a test context xml file with the beans required for your test case.

If you use maven, place the test-context.xml under src/test/resources folder.

Annotate your required test class with the following annotation

@ContextConfiguration(locations = "classpath:test-application-context.xml")

This helps in loading only specific beans for the test case.

If you have two kinds of test cases, then

@Runwith(SpringJUnit4Runner.class)
@ContextConfiguration(locations = "classpath:test-context-case1.xml")
public class TestClassCase1 {}

@Runwith(SpringJUnit4Runner.class)
@ContextConfiguration(locations = "classpath:test-context-case2.xml")
public class TestClassCase2 {}
Vinay Veluri
  • 6,671
  • 5
  • 32
  • 56
  • 1
    I have considered it but I have a lot of tests so to create a application context for each test will take time. I was hoping to just tell spring what I need from said application context – SandMan Aug 18 '16 at 10:25
  • 2
    is there any annotation based approch for this? – Akhil Surapuram Apr 13 '20 at 04:41
  • Hi, how to write the `test-context-case1.xml` to automatically include all files inside some packages (e.g. com.company.project.module_one)? I know how to do that using `@ComponentScan`, but it seems that this does not work here... Thanks! – ch271828n Jun 27 '20 at 01:32
6

It's not direct answer, so I'd would not mark as solution. But hope it's helpful.

Generally I see three options.

  1. As VinayVeluri answered nicely. Create separate contexts and launch them in every tests separately.

  2. Create context one time per all tests. Just like here: Reuse spring application context across junit test classes It's a big optimization for testing all tests at once.

  3. Mix those two first points. Create one smaller context only for testing purpose. Mock that, what's never is tested but can throw NPE etc. Like here: Injecting Mockito mocks into a Spring bean to boost up context build. And re-use it like in point 2. One time build for all tests. Personally I'd go with that one.

  4. This one waiting for answer about some kind of smart test runner, which creates minimum needed context per test.

Community
  • 1
  • 1
Nickname0222022022
  • 577
  • 1
  • 4
  • 22
  • 2
    Ad. 4: you can't really do that automatically. A bean may run some code when it is created and it's not possible to automatically decide if this code is important or not. While it would be bad coding, you could have a bean which adds some record to a database when created and then some part of the code reads that record later on. As a developer you may be able to spot such dependencies but you can't do it automatically - that would be equivalent to the halting problem or worse. – Michał Kosmulski Aug 18 '16 at 13:07
  • Ad. 4: Although tests does not always tell if application is "correct", but one could automatically generate "proposed minimum context" and try to run test. As long as test passes, context is correct. Have a look at "mutation" testing, which can find out, if your tests are actually testing something, what can be broken... See for example https://pitest.org/ – Lubo Feb 14 '22 at 10:09