|
From: Tom Anderson on 22 May 2010 06:47 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
From: Tom Anderson on 22 May 2010 06:50 On Thu, 20 May 2010, Rhino wrote: > But the same situation applies to things that aren't based on > convenience methods. A list of telephone area codes or the names of all > chiropractors in Ohio or any of a thousand other results from methods > are also "moving targets". I'm really not sure how to test those. These methods get their answers from some source of data, right? So the way you test them is with test data. Rather than plugging the code into the real data, you make a controlled set of test data, plug them into that, and test that they behave in the way you would expect given the test data. You then assume that they'll behave the right way when plugged into real data. Naturally, this means structuring your code so that it can easily be plugged into different sources of data. This is probably a good thing anyway. tom -- They entered the Vortex and the dreams became reality
From: Patricia Shanahan on 22 May 2010 10:18 Tom Anderson wrote: > On Thu, 20 May 2010, Rhino wrote: > >> But the same situation applies to things that aren't based on >> convenience methods. A list of telephone area codes or the names of >> all chiropractors in Ohio or any of a thousand other results from >> methods are also "moving targets". I'm really not sure how to test those. > > These methods get their answers from some source of data, right? > > So the way you test them is with test data. Rather than plugging the > code into the real data, you make a controlled set of test data, plug > them into that, and test that they behave in the way you would expect > given the test data. You then assume that they'll behave the right way > when plugged into real data. > > Naturally, this means structuring your code so that it can easily be > plugged into different sources of data. This is probably a good thing > anyway. This is the other side of the principle I was advocating in another subthread, making an output method use a PrintStream that is either a method parameter or provided when its object is constructed, rather than writing to e.g. System.out. I feel that test driven design, by encouraging separation of the details of data input and output processing from the choice of data source or sink, leads to better structured, more flexible code. Patricia
From: Lew on 22 May 2010 11:15 Tom Anderson wrote: > @Test > public void getLocalesReturnsAllLocales() { > Collection<Locale> locales = RhinoUtils.getLocales(); > Locale[] referenceLocales = Locale.getAvailableLocales(); > assertEquals(referenceLocales.length, locales.length); locales.size() > for (Locale locale: referenceLocales) { > assertTrue(locales.contains(locale)); > } > } -- Lew
From: Tom Anderson on 22 May 2010 12:52
On Sat, 22 May 2010, Lew wrote: > Tom Anderson wrote: >> @Test >> public void getLocalesReturnsAllLocales() { >> Collection<Locale> locales = RhinoUtils.getLocales(); >> Locale[] referenceLocales = Locale.getAvailableLocales(); >> assertEquals(referenceLocales.length, locales.length); > > locales.size() Apologies. I knew i was running a risk by posting uncompiled code in this particular thread, but i really thought i'd got away with it this time. tom -- The world belongs to the mathematics and engineering. The world is as it is. -- Luis Filipe Silva vs Babelfish |