Gordon Dickens is an instructor, mentor & consultant. Gordon is currently architecting and teaching several official SpringSource courses and actively tweets about open source technology at http://twitter.com/gdickens. Gordon is active within the Spring Framework community focussed on: Spring training, Spring Roo, Spring Integration, Spring Batch and Eclipse Virgo OSGi projects. Gordon is a DZone MVB and is not an employee of DZone and has posted 39 posts at DZone. You can read more from them at their website. View Full User Profile

JUnit & Spring – What You Don’t Know

01.14.2011
| 20409 views |
  • submit to reddit
When using JUnit in Spring there are several features added that many developers are not aware of.

First, if you are including the Spring Context in your tests, it becomes an Integration Test, no longer a Unit Test.

1. Default Searching of Context File(s)

To instruct Spring to load beans for the tests in the class, we annotate the class with @ContextConfiguration

1a. No File Specified

@ContextConfiguration – with no parameters, (by Default) looks for the config file as the same name as the class with the suffix “-context.xml“. For example,

Using

package com.gordondickens.sample;

@ContextConfiguration
public class UtilsTest {
...
}
...

Is equivalent to

...
@ContextConfiguration("classpath:/com/gordondickens/sample/UtilsTest-context.xml")
...
Will look for a file called UtilsTest-context.xml in the Classpath under the same package as the test. If we are using the standard Maven structure, Spring will search for:

 

  • src/test/java/com/gordondickens/sample/UtilsTest-context.xml
  • src/test/resources/com/gordondickens/sample/UtilsTest-context.xml
  • src/main/java/com/gordondickens/sample/UtilsTest-context.xml
  • src/main/resources/com/gordondickens/sample/UtilsTest-context.xml
1b. File specified (without a starting Slash)

Using

package com.gordondickens.sample;

@ContextConfiguration("utils-context.xml")
public class UtilsTest {
...
}
...

Is equivalent to

 ...
@ContextConfiguration("classpath:/com/gordondickens/sample/utils-context.xml")
...

 

Spring searches for utils-context.xml in each classpath’s directory for package and does NOT traverse subdirectories (packages).

1c. File specified with a Starting Slash

One Simple change can ruin your whole day! Add a Starting Slash to the file name.

Using 

  ...
@ContextConfiguration(value = "/META-INF/spring/utils-context.xml")
...

is equivalent to 

...
@ContextConfiguration("classpath:/META-INF/spring/utils-context.xml")
...
1d. Multiple Files

Pulling multiple configuration files into the application context for your tests.

...
@ContextConfiguration(locations = {"utils-context.xml", "app-context.xml"})
...

 

Best Practice Tip

Create an XML file per test that imports only the application’s context files that are needed.
This can save test execution time, where we only load beans necessary for these tests.

2. Spring Aware Test Options

We can load configuration files for the tests, but we want the added benefit of using Spring Annotations.

NOTE: This requires JUnit 4.5 or later.

Annotate the test class with @RunWith(SpringJUnit4ClassRunner.class).

2a. Autowiring Beans
@Autowired Autowire (inject) in dependencies

Tip: If you want access to the ApplicationContext you can simply autowire this into your test class.

 ...
@Autowired
ApplicationContext applicationContext;
...

2b. Transactional Test Methods

@Transactional At the method level, method is transactional. Class level, all methods are transactional
@TransactionConfiguration Define default transaction parameters for the class
@Rollback Change rollback settings for methods
@BeforeTransaction Method to execute before a transaction
@AfterTransaction Method to execute after a transaction

NOTE: Transactions are rolled back by default in tests. Allowing repeat execution to perform the same db actions.

2c. Profiles - Evaluating Environment
@ProfileValueSourceConfiguration Class level settings for all tests, defaults to system properties
@IfProfileValue Specify name and value(s) that must match to execute the test method
2d. Timeout, Repeating & Invalidating Context
@Timed Defined maximum time that method has to execute
@Repeat Specify the number of times the test method will repeat
@DirtiesContext At class or method level, mark the app context as dirty and should be closed. The app context will be recreated for subsequent test

From http://gordondickens.com/wordpress/2011/01/07/junit-spring-what-you-dont-know-about/

Published at DZone with permission of Gordon Dickens, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Andrew Spencer replied on Fri, 2011/01/14 - 4:23am

Also works with TestNG.

Re the best practice tip: I'm just wondering, why do you recommend a per-test XML file rather than just listing the files you need in @ContextConfiguration(locations={...})? That achieves the same effect, and saves a layer of indirection.

Norris Shelton replied on Fri, 2011/01/14 - 8:10am in response to: Andrew Spencer

Do you mean have one large file that knows how to build everything?  That is what we do and it definitely increases the test time.  All of the objects have to be created, even though I am only running the tests in a package or a class or even a single test. What happens when you try to run multiple tests when using tests contexts per file?  My guess is that each context has to be built.  That would make it even worse.  Maybe a better alternative is a test context per package.  I sense a prototype coming on!

Tomasz Nurkiewicz replied on Fri, 2011/01/14 - 12:08pm

I agree with Norris, having one huge test context file (even when it contains 99% of application beans and takes quite a lot of time to startup) is better than having a separate small context per test - so I wouldn't recommended it as a best practice. Recently we reduced build time on our CI server from 20 minutes to less than 10 just by having one reusable context. See my article about test context caching.

Roger Parkinson replied on Fri, 2011/01/14 - 4:06pm

Multiple contexts for me. Otherwise my tests are dependent on other people's contexts and when they are broken so is my test. I may combine several tests from the same package, usually each have their own context and include a common one. Everyone else I work with does the one huge context thing (so I haven't convinced them) but I have endless trouble when I have to work on their code because of broken tests.

In our environment speed of unit tests is not much of an issue, probably because we don't write enough of them.

Liam Knox replied on Fri, 2011/01/14 - 10:53pm in response to: Tomasz Nurkiewicz

Absolutely not. A unit test should be configured with what it is testing. You should not be merging concerns of other tests in an effort to marginally improve performance. Basically you are invalidating your tests. You wouldn't advise creating random objects for a test that doesn't use these, so I wouldn't advise creating random beans either

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.