Introducing MagicTest

May 28, 2009

If you’ve written enough JUnit 4 tests, then you should be familiar with code that looks like:

public class FooTest {

  private Foo foo;

  @Before
  public void setUp() {
    foo = new Foo();
  }

  @Test
  public void testBar() {
    assertTrue(foo.bar());
  }
}

Now, how often have you wished it were possible to simply go straight to @Test, do not pass setUp(), do not have to new Foo()?

Introducing MagicTest

So I came up with MagicTest, a parameterized base class for JUnit 4 tests that’ll let us do just that.
It lets us rewrite the above simply as:

public class FooTest extends MagicTest<Foo> {

  private Foo foo;

  @Test
  public void testBar() {
    assertTrue(foo.bar());
  }
}

It was bound to happen. I was bound to put to use my ‘hack’ on how to discover the type parameter to a generic base class.

Without further ado, here’s a simplified version of MagicTest:

public class MagicTest<T> {

  private final Class<T> classUnderTest;

  @SuppressWarnings("unchecked")
  public Base() {
    Class<?> actualClassOfSubclass = this.getClass();
    ParameterizedType parameterizedType = (ParameterizedType) actualClassOfSubclass.getGenericSuperclass();
    Type firstTypeParameter = parameterizedType.getActualTypeArguments()[0];
    this.klazz = (Class) firstTypeParameter;
  }

  @Before
  public void findAndInstantiateObjectToTest() throws Exception {
    for (Field field : this.getClass().getDeclaredFields()) {
      if (field.getType().equals(classUnderTest)) {
        T object = classUnderTest.newInstance();
        boolean accessible = field.isAccessible();
        if (!accessible) {
          field.setAccessible(true);
        }
        field.set(this, object);
        field.setAccessible(accessible);
      }
    }
  }
}
 

That really wasn’t too hard, now, wasn’t it?

Why call it MagicTest? Well, because I also happen to like using the Mockito mocking framework, and I happen to already have a unit test base class called MagicMocker. Get it? 😉

Advertisements

5 Responses to “Introducing MagicTest”

  1. David Saff Says:

    Why not just initialize the variable in-line?

    private Foo foo = new Foo();

  2. Alistair Says:

    Hi, David. That’s a very good point—and one that I’ll get to soon enough.

    Actually, this was meant to be ‘ActiveTest’, a test that not only instantiates the object, but also mocks any objects annotated with @Mock, then injects the mocks into the object under test. I guess I was just pleasantly surprised this could all be done in the first place. 🙂

    (Edit: See ActiveTest on dirty-mockito.)

  3. adrian Says:

    @david, in-line will not be repeated for every test case. that means you use the same instance for all the test cases.

    @alistair, is this just for demo? because it is a little restrictive to the subclasses. usually, you do not only instantiate in setup. you do more like initialize default values for testing like foo.setA(“a”). How can you do that in the subclasses? Have another @Before? It is possible but I think it does not guarantee which @Before will be called first (from the base class or subclass).

  4. Alistair Says:

    Hi, Adrian.

    Check out my updated post on ActiveTest which takes this idea further.

    But, yes. The subclass can have its own methods annotated with @Before and JUnit will happily call the setup methods in order: Base#setUp() -> Subclass#setUp() just as you would expect.


  5. @adrian. Unless JUnit has changed recently, the runners will create a new instance of the test class for every test. It was designed that way to ensure test method independence. NUnit, on the other hand, uses the same instance of the test class.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: