If you’ve written enough JUnit 4 tests, then you should be familiar with code that looks like:
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:
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:
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? 😉
Why not just initialize the variable in-line?
private Foo foo = new Foo();
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.)
@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).
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.@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.