Re: Need help designing some JUnit tests
On Thu, 20 May 2010, Rhino wrote:
What is the best way to test a method whose output is unpredictable
because that output is a moving target? For example, I have a method
that uses Locale.getAvailableLocales() to display all of the Locales on
the current JVM. It works fine but how do I write a JUnit test, given
that an upgrade to the JVM could introduce additional Locales?
I'm a bit late to the party here, and i lost track of the answers, but i'd
think about writing a test where the expectation was driven by the set of
locales available in the JVM:
@Test
public void getLocalesReturnsAllLocales() {
Collection<Locale> locales = RhinoUtils.getLocales();
Locale[] referenceLocales = Locale.getAvailableLocales();
assertEquals(referenceLocales.length, locales.length);
for (Locale locale: referenceLocales) {
assertTrue(locales.contains(locale));
}
}
To be honest, though, if all your method is doing is wrapping
Locale.getAvailableLocales, i wouldn't write it in the first place. The
code you don't write is the easiest code to test!
Lastly - and thanks for bearing with me this far - is it normal practice
to write JUnit tests for all methods that are in parent classes of the
class being tested? For example, while writing tests for one of my
classes, the wizard in Eclipse asked if I wanted to produce test methods
to test methods of Object like equals() and wait(). Should I be writing
tests for those methods as well as for those in my own class?
There's no point at all writing tests for wait and notify, because those
are final methods defined in Object, so nothing you do can change whether
they work properly or not.
If you haven't overridden equals from Object, then for a similar reason,
there's no need to test it. If you have overridden it, then of course you
need to test it.
The one time i can think of where you might want to test a non-overridden
method is if it depends on a method you have overridden (or an abstract
method which you've implemented). For example:
public class CollectionOfThings<E> {
public abstract E[] getAllThings();
public E getFirstThingInOrder() {
SortedMap<String, E> sortedThings = new TreeMap<String, E>();
for (E thing: getAllThings()) sortedThings.put(thing.toString(), thing);
return sortedThings.values().iterator().next();
}
}
public class CollectionOfLocales extends CollectionOfThings<Locale> {
public Locale[] getAllThings() {
return Locale.getAvailableLocales();
}
}
In that case, when testing CollectionOfLocales, you might want to test
getFirstThingInOrder. Like:
@Test
firstLocaleInOrderIsRight() {
assertEquals(
new Locale("ar", "AE"),
new CollectionOfLocales().getFirstThingInOrder());
}
The point would really be that the contract of CollectionOfLocales with
its subclasses requires that the things they provide have toString methods
which return strings which preserve the ordering of the things. You need
to test that this term of the contract is fulfilled by each subclass.
Whilst this case may seem trivial, note that a CollectionOfAges subclass
which supplied Integers would not meet the requirement, because the
ordering of Integers' toStrings do not reflect their numerical order. Such
a class would be intrinsically broken, and this unit test would catch
that. Fixing it would probably involve changing the contract between
CollectionOfThings and its subclasses, and the implementation of
getFirstThingInOrder.
Oh, also note that the code i wrote above for Locales is wrong. I only
discovered this when i actually compiled and ran it (or something a bit
like it, at least - not the exact code above). This illustrates the
importance of tests!
tom
--
They entered the Vortex and the dreams became reality