|
From: Eric Sosman on 20 May 2010 17:25 On 5/20/2010 4:47 PM, Rhino wrote: > Eric Sosman<esosman(a)ieee-dot-org.invalid> wrote: >> [...] >> For constructors, what you want to test is that they throw >> exceptions when they're supposed to (e.g., InvalidArgumentException), >> and that the newly-constructed object satisfies all the invariants >> it's supposed to. In your no-argument Foo() constructor exceptions >> seem unlikely (possible, though: think HeadlessException), but you >> might check `myFoo.getHeight() * myFoo.getWidth() == myFoo.getArea()' >> or whatever. >> > Could you expand on this last paragraph a bit. The part about verifying > that exceptions get thrown at the appropriate time are fine but I'm not > clear on what you mean by the invariants that it is supposed to satisfy. > The last sentence is almost not quite clear. Are you saying to find some > aspect of the work that is done in the constructor and verify that it > took place, so that if it is drawing a GUI component, that the component > exists and has dimensions greater that 0 x 0? What if the constructor > does very very little - maybe just a super() - or even nothing at all? In > those cases, is it sufficient to just do Read the Javadoc for your Foo class, and consider what properties a newly-constructed Foo instance should satisfy. Test that they are satisfied -- that is, test that a newly- constructed Foo complies with its "contract." > if (Foo == null) fail("Constructor failed to instantiate the class"); Rhino, if you keep on spewing this sort of codecrap I'm going to shove that horn of yours firmly up the orifice that spews. >> You should test that the factory method behaves as advertised. >> If it can return null under some circumstances, you should check that >> it does so when it's supposed to and does not when it isn't. If it >> returns non-null, the thing returned will necessarily be of the type >> declared for the factory method -- but not necessarily of that "exact" >> type, as it might be a subclass or any arbitrary implementation of an >> interface type. That may make a difference in what you test. >> > Can you elaborate on this a bit? Can you show me a simple example of a > constructor returning a subclass or implementation of an interface? No, because a constructor cannot do such a thing. But you seemed to be talking about a factory method (although it's hard to be sure from reading your codecrap), and a factory method -- any method, in fact -- can return anything compatible with its declared type. Wasn't it you who had the problem with SpinnerNumberModel recently, where the getNumber() method sometimes returned a Short, sometimes a Long, sometimes something else? >> I'd say it's unnecessary to test wait() and notify() and other >> final methods of Object. More generally, it's probably unnecessary >> to test final methods of any superclass. >> >> But equals() is not final, and if the class being tested has its >> own equals() you should test it. (Note that it's extremely rare to >> inherit equals() from a superclass unless you're inheriting it all >> the way from Object undisturbed.) If you were writing tests for >> Integer, you might test `new Integer(42).equals(new Integer("42"))', >> for example, and `! new Integer(42).equals(new Integer("-42"))'. > > So, in a nutshell, only test the methods of the parent classes if I > overrode them; otherwise, don't worry about them. That makes sense to me! There's a subtle point there, a conflict between "black box" and "clear box" testing. The only way you can *know* that a subclass inherits a non-final method rather than overriding it is to peek into the subclass' implementation (either by looking at the source or by using reflection). But what if somebody comes along next week and decides to override a method you decided not to test, on the grounds that it was not overridden? One thing you might do is run some of Super's unit tests on Sub instances. Another might be to include a "sanity check" test in your Sub, something that reflects on Sub and verifies that the methods you've chosen not to test are in fact inherited. Finally, you've got to realize that unit testing, important as it is, is not the be-all and end-all of verifying correctness. -- Eric Sosman esosman(a)ieee-dot-org.invalid
From: Jim Janney on 20 May 2010 18:02 Rhino <no.offline.contact.please(a)example.com> writes: > I'm getting more and more comfortable with JUnit after a long absence > from it but I'm still struggling with composing tests for some > situations. If anyone can help with any of these situations, I'd love to > hear your suggestions. > > -------------------------------- > > Scenario 1 - Moving Targets > > 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'd have the same > kind of problem if a method was returning a list of area codes or cell > phone providers. There must be some standard way of writing a JUnit test > for that but I'm drawing a blank. Strictly speaking, unit testing means testing small bits of code in isolation -- you break a program down into the smallest pieces that can be tested independently (in Java this generally means a class) and verify that each one satisfies its public contract. The public contract is whatever it is that says how the class is supposed to behave. In some languages (Eiffel and, um, uh, ...) this can be formally specified in the language itself but more usually it's some written description or just an idea in the programmer's mind. In Java it may be in the Javadoc (the JRE classes are pretty good about this). For example, the Javadoc for Locale.getAvailableLocales() specifies that the result must contain Locale.US, so you could sensibly test for this in a unit test. If your application depends on the presence of other locales you may want to test for those also. Once you move past this you're no longer doing unit testing but some sort of integration testing. This is also valuable but the rules are different so it's useful to remember which kind of testing you're doing. If you have a class that finds, say, all the zip codes in a given city, and it gives you a wrong answer, where is the problem likely to be? In the class or in some database that it's searching? You can verify that the class searches the data correctly but not that the data is correct. Or perhaps you can, but that really belongs in some other test. -- Jim Janney
From: Lew on 20 May 2010 19:17 Rhino wrote: > Actually, my getLocales() method is really just a convenience method that > massages the results of Locale.getAvailableLocales() itself. > > Just to be sure I'm using the term "convenience method" correctly, I'm > referring to a method I write that uses existing Java API methods but > that combines several lines of code into one or two. For example, since I Yep. > prefer my Locales list to be in alphabetical order, I've written this: > > public Map<String, String> getLocales() { > > Locale[] listOfLocales = Locale.getAvailableLocales(); > > Map<String, String> locales = new TreeMap<String, String>(); > for (Locale singleLocale : listOfLocales) { > locales.put(singleLocale.toString(), singleLocale.getDisplayName > (locale)); Umm, what is 'locale' in this line? I mean, it's obvious that it's a 'Locale', but what is it? > } > > return locales; > } Your 'listOfLocales' variable is, perhaps, not necessary. That idiom also works if you want to retrieve the Locales themselves based on name: Map <String, Locale> locales = new TreeMap <String, Locale> (); for( Locale loc : Locale.getAvailableLocales() ) { locales.put( loc.getDisplayName(locale), loc ); } or something like. > As such, I don't know how to do a JUnit test on it, specifically how to > generate an expected result that can be compared to my actual result. It > seems self-evident that I have to get my expected result in a different > way than I get the actual result, otherwise, I'm not proving anything. Unit tests cannot do everything that the class under test does, otherwise the unit test class would be the class under test. What unit tests do is test the "happy path" and various corner cases to provide a high level of certainty that the tested class will correctly handle the infinite variety of stuff thrown at it in production. In other words, a unit test is really a sanity check that takes care of the most likely issues. If a unit test could prevent all possible errors, we'd never need logging. For your case, you might test that the 'Map' has the same number of entries as 'getLocales()' has elements and that it correctly returns the right values for some representative keys. .... > Or is it the case that such a method CAN'T have its accuracy tested in > this way and no such attempt should be made? Is it enough to prove that > the method executes without throwing an exception? You should go farther than that. >>> -------------------------------- >>> >>> Scenario 3 - getInstance() >>> >>> Given a hypothetical class named Fuzz where the constructors and >>> getInstance() methods are: >>> >>> private Fuzz() { >>> // do something >>> } >>> >>> private Fuzz(Locale locale) { >>> // do something >>> // initialize instance variable for locale >>> } >>> >>> public getInstance() { Lew wrote: >> Where is your return value? >> >> This won't even compile. >> Rhino wrote: > Sorry, I just hacked that together to save a minute. I probably should > have copied in a compiled example.... Oopsie. (Giggle) >>> Would the following be adequate as JUnit tests for the getInstance() >>> methods? >>> >>> public void testGetInstance() { >>> >>> Fuzz fuzz = Fuzz.getInstance(); >>> if (!fuzz instanceof Fuzz) fail("Failed to instantiate Fuzz"); >>> } > ... > So, if the constructor doesn't > throw any exceptions and is public, you say that I should test that "the > returned values exists and is not null". Non-nullity of the return value should be handled by an 'assert' in the factory method, and therefore not necessary to test in the unit test. Existence is already guaranteed by the successful return of the factory method. > What's my best way of doing that? Am I right in assuming that a simple > > if (Foo != null) Ummm, 'Foo' is a class, right? It better be, and therefore that line will not compile. > will cover both of those? How about 'assertNotNull( fuzz );'? -- Lew
From: Lew on 20 May 2010 19:27 Rhino wrote: >> if (Foo == null) fail("Constructor failed to instantiate the class"); Eric Sosman wrote: > Rhino, if you keep on spewing this sort of codecrap I'm going > to shove that horn of yours firmly up the orifice that spews. Rhino, the source of Eric's irritation is that despite apologizing profusely multiple times for posting uncompilable code, you did it again anyway. Apologies don't help much if you don't correct the behavior. Also, never omit the curly braces in the statement bodies of 'if', 'while', 'for' and 'do...while' statements. You'd best adhere to the discipline of providing only SSCCEs for a while <http://sscce.org/> until you gain more familiarity with the syntax rules for Java. You won't know that it's an SSCCE unless you actually at least *compile* the code you post, and really, you should run it first, too. Doing that will help you. Failing to do that will cause the smartest and most helpful respondents, such as Eric, not to. -- Lew
From: Patricia Shanahan on 21 May 2010 04:15
Rhino wrote: .... > Actually, my getLocales() method is really just a convenience method that > massages the results of Locale.getAvailableLocales() itself. > > Just to be sure I'm using the term "convenience method" correctly, I'm > referring to a method I write that uses existing Java API methods but > that combines several lines of code into one or two. For example, since I > prefer my Locales list to be in alphabetical order, I've written this: > > public Map<String, String> getLocales() { > > Locale[] listOfLocales = Locale.getAvailableLocales(); > > Map<String, String> locales = new TreeMap<String, String>(); > for (Locale singleLocale : listOfLocales) { > locales.put(singleLocale.toString(), singleLocale.getDisplayName > (locale)); > } > > return locales; > } > > As such, I don't know how to do a JUnit test on it, specifically how to > generate an expected result that can be compared to my actual result. It > seems self-evident that I have to get my expected result in a different > way than I get the actual result, otherwise, I'm not proving anything. You seem to be assuming that a JUnit test requires an expected result. Don't forget the assertTrue method, which lets you test arbitrary conditions. Patricia |