Matt RaibleMatt Raible is a Java Champion and Developer Advocate at Okta. developer.okta.com

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

Make your JUnit Tests run faster when using Spring

JRoller is down, and has been down for an hour or so - so I've decided to post this Spring Live entry here.

I discovered an interesting thing today about Spring and my JUnit tests. I noticed that the VelocityEngine I was setting on my PositionManager was getting initialized once for each test* method in my Test. This means that since my PositionManagerTest has 10 test methods - it would load the context 10 times.

Loading the context so many times was because the following code was in my Test's parent's constructor:

    ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");

I suppose I expected any constructor-iniatialized variables to be initialized once and only once. So I figured out a solution to make my JUnit tests run faster. By making the ctx variable static, and loading the file in the member variables definition, I greatly reduced the amount of time needed to run tests. Below is the new code I'm using:

    protected static ApplicationContext ctx = 
    	new ClassPathXmlApplicationContext("/applicationContext.xml");

By doing this, the ApplicationContext is only set once, and my tests run much faster. Here's some performance comparisons from Struts Resume:

Average time to run "ant test-dao": 36 seconds
Average time to run "ant test-dao" after this change: 26 seconds

A 10 second improvement - that's crazy talk dontcha think?! I've tried it on single tests, as well as suites - and it seems to improve performance by approximately 30% across the board.

Because of this experience, I have to recommend that when you write JUnit tests that use Spring - you should initialize your ApplicationContext in a static member variable. It seems to be the best performing and logical choice. Of course, if I'm off my rocker - please let me know.

On a sidenote, it would be cool if Roller allowed me to turn off comments for a single post. I like how Simon posts stuff on java.net and then aggregates it to his personal weblog.

Posted in Java at Apr 08 2004, 01:22:14 AM MDT 21 Comments
Comments:

Many people have remarked on JUnit's apparent inefficiency when it comes to dealing with the setUp() method. What you have to realise is that is was a concious design decision made to call setUp() prior to each test call in order to isolate one test from another. If setUp() was called only once, there is a potential for one test to impact on another by changing common data. Likewise by using static data, you are opening yourself to the possibility that testOne could manipultae your static data, and testTwo would then be operating in a different environment. In this specific instance you should be OK, however its probably not a pattern you want to use too often as it may well bite you on the ass further down the line.

Posted by Sam Newman on April 08, 2004 at 02:27 AM MDT #

A little more on my blog

Posted by Sam Newman on April 08, 2004 at 03:11 AM MDT #

!You can simly use junit.extensions.TestSetup class for that purposes For example i am using in-container Jetty server to run WebUnit tests and Jetty started only once, not for every test. It works like a dream. So there is my JettyTestSetup [{Java2HtmlPlugin public class JettyTestSetup extends TestSetup { protected static Server server; public JettyTestSetup(Test test) { super(test); } protected void setUp() throws Exception { server = new Server(); server.addListener(new InetAddrPort(9000)); WebApplicationContext webapp = server.addWebApplication("/", "context"); webapp.setDefaultsDescriptor("config/webdefault.xml"); server.start(); } protected void tearDown() throws Exception { System.out.println("Stopping server..."); server.stop(); } } }]

Posted by wassup on April 08, 2004 at 03:36 AM MDT #

JRoller should be back up, I'm not sure why it died, perhaps something is leaking memory.

Posted by Matthew Schmidt on April 08, 2004 at 04:12 AM MDT #

Hmm...sent a trackback from my blog post but it didn't appear here...

Posted by Sam Newman on April 08, 2004 at 04:28 AM MDT #

I think that testing beans inside an initialized container is more "integration" than "unit" testing. In our project we have a similar structure: in the setup part we have a context that initializes hibernate mappings, which is unfeasible at every test run. So we developed a structure with a TestSetup which initializes the context once, and then passes it to every test case. The next idea would be having a concept of "spring-managed test suite", where the test cases are actually ioc-managed beans, and receive with dependency injection the beans they are supposed to test: I've tried working at it but have had to stop due to my project time constraints. I think it is a pretty natural idea, though, when working with spring and hibernate: unit tests are a good thing, but after them you need to test components with their actual interactions, in integrated tests. Moreover, a structure like this one allows to test Hibernate DAOs and make sure that HQL queries aren't broken. In the future I'd like to work on a "spring-unit" project which deals with these issues: I've already got some classes that help creating blocks of hibernate persistent objects at startup and removing them at teardown: this is pretty helpful, for integration tests.

Posted by Davide Baroncelli on April 08, 2004 at 06:20 AM MDT #

I agree the line between unit and integration/functional tests is not always a clear one. You could argue that Matt could Mock out the application context, however if you assume that all external tools are 'safe' then using Spring to produce the context is an acceptable time saving (from the point of view of writing the test). LEts put it another way - what if my test calls a utility class? On the one hand I'm effectively testing the utility class itself, but I tend to be pragmatic about such things. If the other classes I'm using are part of the same project I'll mock them out, if its an external API I'm more inclined to allow their use.

In the case of your Hibernate DAO's for example, in testing the DAO's themselves I would contact the database via Hibernate. If I was using the DAO in a different test I would mock them out rather than contacting the DB - if you assume that the DAO has loaded from the DB you put yourself in the situation of being unable to have several tests run and fail (which is possible if all tests are isolated) - if your DAO fails to load, tests which rely on them will also fail.

Posted by Sam Newman on April 08, 2004 at 06:55 AM MDT #

There are two bugs in the ant bugzilla for this: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=10413 and http://nagoya.apache.org/bugzilla/show_bug.cgi?id=24697

Posted by Haroon Rafique on April 08, 2004 at 07:42 AM MDT #

Well, I think that the idea of IOC is that you don't have to mock out the context: if you need unit testing a bean that depends on other beans, it's those other beans that you should mock, and pass them to the bean under test. The problem that arises with context creation, is: "given that I want to test a number of objects with their dependencies, how do I create a testing environment where that context is correctly initialized (once) when I do execute my tests?". The problem is not only about dependencies: in Spring, for example, a good number of functionalities are provided declaratively or via AOP features. You want to unit test the business logic in beans, but I personally want to test those beans in their context, where they have their behaviour completed by the AOP infrastructure. This is not only true for integration tests, but also for performance tests: in the integration test suite I began working with, there are classes that help running a number of tests in parallel threads and measure the overall throughput: this only makes sense when the logic is completed by spring features. Another issue: if you use the HibernateInterceptor in order to have automatic Hibernate session closing, your bean logic is not complete, without that interceptor: so your test should take it into account: you could impose the interceptor by writing code that manually weaves it's behaviour into the classes you are testing, but since this is already written in the applicationContext.xml and it's "normal" for your classes being executed in the context, then the natural idea is for them to be tested in that context. I agree that pure business logic should be tested in isolation, but code that needs to be performance-tested against database access must be tested in that way (if you are interested, for example, in testing the services layer - i.e. business logic + data access - in a web-layer independent way, of course, if not you could probably directly test it with web-testing tools such as astra load tester or similar stuff).

Posted by Davide Baroncelli on April 08, 2004 at 07:50 AM MDT #

I commented on this idiotic behavior here: http://www.beust.com/weblog/archives/000082.html In short, JUnit instantiates your whole test class every time before invoking a test method. Makes you wonder why there is a setUp() method at all, uh? It's really sad we are all stuck with such a poorly designed and implemented project :-(

Posted by Cedric on April 08, 2004 at 08:52 AM MDT #

Time for a junit replacement? After all it's such a simple framework that it should not be difficult not only to rewrite, but also for ide providers to implement... Although: is the "instantiate the test class every time" actually inevitable? Maybe one could only rewrite TestSuite and make it behave in a different way. After all it plugis into junit because it's just another implementation of the Test interface, isn't it? That's what I was thinking about when I wanted to provide a spring-integrated test suite: one that does not directly instantiate test objects but asks them to the pre-initialized container.

Posted by Davide Baroncelli on April 08, 2004 at 09:12 AM MDT #

A replacement? Why not just fix the bug!

Posted by Sam Newman on April 08, 2004 at 09:15 AM MDT #

I'm afraid "it's not a bug, it's by design".

Posted by Davide Baroncelli on April 08, 2004 at 09:19 AM MDT #

setUp() being called multiple times (once per test) is certainly correct and I agree 100% with that. However, instantiating the class multiple times is by design? I cannot understand the rational for that considering the presence of setUp() and tearDown()...

Posted by Sam Newman on April 08, 2004 at 09:46 AM MDT #

Well, the problem lies in the design of TestSuite, which is the class one normally uses in order to execute tests. TestSuite looks at the classes you give it, and extracts all testXXX method names, then it instantiates a new class setting its "name" attribute to the "testXXX" method name. When the "run" method will be called on that class, that name attribute is used in order to retrieve the method with reflection and execute it. Actually, the design is quite complex, and the general feeling it gives is that the same thing could be done in a more clean way... But what I don't understand is: for what I know in Junit, one could simply reimplement TestCase and TestSuite with classes that implement the Test interface, and we would have a totally different JUnit with different rules, but just as integrated with the ides as the current is (as long as the ides use the public Test (and TestResult) interface. So, (nearly) everything should be possible, as long as "replacing the custom JUnit behaviour" is the matter. Am I wrong?

Posted by Davide Baroncelli on April 08, 2004 at 11:12 AM MDT #

This feature, as others have said is by design. The problem stems from the fact that just because we have setUp() and tearDown() methods, what stops someone from instantiating an object in the constructor and sharing it in your test methods that way? When you do that, who cares if you have setUp() and tearDown() you've just bypassed their use/purpose. (That's what Matt actualy tried to do in his example). So, if you think about it, the design is actualy pretty simple and it works to enforce test isolation behaviour.

Of course you are then left with the problem Matt wrote about, which in the way the he solved it for this particular scenario is not a bad idea at all.

Personaly I would even avoid doing this as it is starting to look more like an integration test then a unit test.

Posted by John Cavacas on April 08, 2004 at 11:30 AM MDT #

I investigated about 5 minutes into looking at TestSetup as described in Pragmatic Unit Testing. They had an example where the TestCase would call setUpOnce() and tearDownOnce() methods. I couldn't get it working, so I figured to go the easiest route. It works - which is more important than anything.

The design I'm using (loading applicationContext.xml in setUp()) is actually something I got from Spring's PetClinic app. I (personally) feel it's important to use Spring's wiring abilities in my unit tests, rather than re-wiring them myself - or what am I testing? Method calls and return values? Those seem to simple to really need testing.

I agree that it would be cool to dynamically mock my dataSource, but it's really not that hard to use HSQL or MySQL to do the real thing. That being said - speeding up my unit tests is one of my primary concerns in the next few months. I'm sure mocking will help that. Therefore, my quest for knowledge continues.

It would be cool if you could somehow tell Spring to use a Mock programmatically in your unit test and have it wire that instead of what's in the applicationContext.xml. Maybe you can and I just need to be enlightened.

Posted by Matt Raible on April 08, 2004 at 12:10 PM MDT #

What about using junitx.util.ResourceManager, its just a simple static map underneath.

Posted by Francisco Hernandez on April 08, 2004 at 12:42 PM MDT #

I thank u greatly foryour good information . And as i'm A teacher of english in the Middle east I need more

Posted by essam on February 19, 2006 at 04:02 AM MST #

I use static ApplicationContext and it works fine. I also need to have a oneTimeTearDown, and then I came over the method in TestCase called countTestCases. I then used it like this in my SuperTestCase
protected static ApplicationContext ctx = null; // Matt's tip
private static int countTestCases = 0;
 
protected void setUp() throws Exception {
	super.setUp();
		
	if(countTestCases == 0) {
		log.info("oneTimeSetUp()");
		String [] files = 
		{
			"commons-context.xml",
			"jdbcDao-context.xml"
		};
		ctx = new ClassPathXmlApplicationContext(files);
		
		// do some setup

		countTestCases = this.countTestCases();
	}
}

protected void tearDown() throws Exception {
	--countTestCases;
	if(countTestCases == 0) { // no more tests, tear down now
		log.info("oneTimeTearDown()");
		
                       // do some tear down
	}
}
I know it is hack, but it gives me the ability to actually have the oneTime feature, and I do not have to create a suite and use the TestSetup decorator thing which could have been an alternative. I use Maven as build tool and I only create separat testcases not run under a Suite so the TestSetup extend is not an option.

Posted by Ronny Næss on September 06, 2006 at 02:44 AM MDT #

Seems like this was not so good idea after all. I'm sure it worked yesterday, but that was before I rearranged a lot of code. Anyway, I came up with another solution. Using reflection and a filter search for spesific methods (prefix 'test') I am able to count the tests to run for each case. This is how mye classes is linked to each other. SpringTestCase <- DatabaseTestCase <- JdbcDaoTest

The SpringTestCase
public abstract class SpringTestCase extends TestCase {
	private static Log log = LogFactory.getLog(SpringTestCase.class);
	
	// Setting ApplicationContext to static so the system can bypass the setUp run for each test! Tips from Matt Raible
	// More info at http://raibledesigns.com/page/rd?entry=make_your_junit_tests_run 
	protected static ApplicationContext ctx = null;	
	public static int countTestCases = 0;
	protected String [] files = null; 
	
	protected void setUp() throws Exception {
		if(countTestCases == 0) {
			springInit();
			oneTimeSetUp();
		    if(log.isDebugEnabled())
		    	log.debug("Test Case cout = " + countTestCases );
		}
	}
	
	protected void tearDown() throws Exception {
		--countTestCases;
		if(countTestCases == 0) {
			oneTimeTearDown();
		}
	}
	
	/**
	 * Anything that should be ran only once (test start) can be put here
	 * @throws Exception
	 */
	protected void oneTimeSetUp() throws Exception {
		log.info("oneTimeSetUp()");
		
		// do something like insert test data into database
	}

	
	/**
	 * Anything that should be ran only once (test end) can be put here
	 * @throws Exception
	 */
	protected void oneTimeTearDown() throws Exception {
		log.info("oneTimeTearDown()");

		// do some one time tear down
	}

	/**
	 * Please add configLocations to property files here to make Spring work.
	 *
	 */
	protected void setUpConfigLocations() {
	
		
	}
	
	private void springInit() throws Exception {
		log.info("Configuring spring");
		try {
			setUpConfigLocations();
			if(files == null)
				throw new NullPointerException("Please declare spring configLocations (property files)");
			ctx = new ClassPathXmlApplicationContext(files);
		} catch(Exception e) {
			log.fatal(e);
			throw e;
		}
	}
	
	/**
	 * Method that counts methods starting with method name prefix
	 * @param prefix the methodname startswith
	 * @param clazz the class under count
	 * @return number spesifying amount of countet methods which name starts with prefix
	 */
	protected int countMethods(String prefix, Class clazz) {
		Method [] methods = clazz.getMethods();
		int counter = 0;
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if(method.getName().startsWith(prefix)) {
				counter++;
				log.info(method.getName());
			}
		}
		return counter;
	}
}
The DatabaseTestCase
public abstract class DatabaseTestCase extends SpringTestCase {
	
	private static Log log = LogFactory.getLog(DatabaseTestCase.class);

	protected void setUpConfigLocations() {
		String [] configLocations = 
		{
				"jdbcDao-context.xml",
				"commons-context.xml"
		};
		files = configLocations;
		
	}
}
The JdbcDaoTest
public class ArticleJdbcDaoTest extends DatabaseTestCase {

	private static Log log = LogFactory.getLog(ArticleJdbcDaoTest.class);
	private ArticlesDao dao = null;
	
	protected void setUp() throws Exception {
		super.setUp(); // important to do this!
		dao = (ArticlesDao)ctx.getBean("articleDao");
	}
	
	protected void oneTimeSetUp() throws Exception {
		super.oneTimeSetUp();
		countTestCases = countMethods("test", ArticleJdbcDaoTest.class);
	}
	
	// All tests below here
}
I'm sure there is possible to optimise this a lot, but I think I will stop here and I have used way more time figuring how to pass this problem anyway.

Posted by Ronny Næss on September 07, 2006 at 04:44 AM MDT #

Post a Comment:
  • HTML Syntax: Allowed