July 24, 2004

Configuration injection in tests

Why does JUnit force you to have test() and setUp() methods that don't have any parameters?

Because there is no easy way in Java 1.4 and under to specify the values for these parameters.

As a consequence to this shortcoming, various JUnit add-ons have added the possibility to look up property files in your setUp() method which are then initialized inside your test method (and incidentally, will be initialized over and over again for each invocation of the test method, as I reported in an earlier JUnit bug report).

A better way to address this problem is to use annotations as a bridge between your Java code and a property file.  I just added support for this in TestNG, and here is how it works (excerpt from the new TestNG documentation).

Parameters

Test methods don't have to be parameterless.  You can use an arbitrary number of parameters on each of your test method, and you instruct TestNG to pass you the correct parameters with the parameters method on the @Test annotation.  For example:

  @Test(parameters = { "first-name" })
  public void testSingleString(String firstName) {
    System.out.println("Invoked testString " + firstName);
    assert "Cedric".equals(firstName);
  }
In this code, we specify that the Java parameter "firstName" of your Java method should receive the value of the property called first-name.  This property is defined in the testng.properties that you used to invoke this test:

#testng.properties
first-name = Cedric

The same technique can be used for the @Configuration annotations:

@Configuration(beforeTestMethod = true, 
               parameters = {"datasource", "jdbc-driver")
public void beforeTest(String ds, String jdbcDriver) {
  m_dataSource = ...;  // look up the value of datasource
}
This time, the two Java parameter ds and driver will receive the value given to the properties datasource and jdbc-driver respectively.  Note that the properties are mapped to the Java parameters in the same order as they are found in the annotation, and TestNG will issue an error if the numbers don't match.

This system allows for a flexible configuration of the runtime configuration of your tests and allow you to arbitrarily define how granular this configuration will be (configuration method or test method).

Posted by cedric at July 24, 2004 06:10 PM

Comments

Yuck! Now someone has to look in multiple files to understand what your test does. The fact that JUnit setUp and test functions take no parameters is a GOOD thing. It forces the programmer to explicitly define a unit's dependencies, which makes it easier for somebody else to understand the test, and thereby understand the class under test. It also makes it hard to test a unit that requires a lot of dependencies and external configuration, which gives valuable feedback about the quality of the design of the class under test.

Posted by: at July 23, 2004 01:50 AM

It would certainly be useful to have parameterized test methods, but passing string properties to the test is not really it as there is no real benefit over acquiring the values at the beginning of the test.
IMO it would provide much more value (and is still understandable) if a test framework would support something like:

public void testSomeDBStuff(DataSource ds) {
// check something against the database
...
}

and the framework supplies the datasource from somewhere. The point here is that to understand the test it is not important how and where the datasource is acquired, but only what the test does with it. And IMO there is no need for annotations here, plain IoC usage or similar would probably suffice.

Posted by: Thomas Dudziak at July 23, 2004 03:35 AM

why not just use a variable-length argument list?

Posted by: Jonathan Ellis at July 23, 2004 05:50 AM

Exactly my thought (Jonathan's).

Posted by: Sumit at July 23, 2004 10:52 AM
Post a comment






Remember personal info?