Re: Basic JUnit questions
On Thu, 18 Mar 2010, Rhino wrote:
I think I understand the basics of using JUnit - I'm still using JUnit3
for the moment
I'd strongly advise moving to 4 right away - there are some differences to
get used to, so best to do that before you get comfortable with 3.
1. For each class that I develop, I should have one JUnit class that
extends TestCase and that this TestCase class should test each of the
methods in the class I developed?
There doesn't have to be a strict 1:1 relationship. Test classes are
classes, and are subject to the same principles of design as any other
classes - give each class a single responsibility, splitting classes that
do too many different things. A responsibility could be "test that the
Furnace class works", but it could also be "test that the Furnace class
incinerates various kinds of rubbish properly", where you'd then have
other classes to test other aspects of Furnace behaviour. You might also
want more integrationish tests, which would involve the interaction of two
classes - test that the ResourceLoader works properly when coupled to the
JDBCDataStore.
2. When I look at the JUnit window in Eclipse to see the results of that
TestCase class, all of them should have a Green icon beside them?
Yes, absolutely.
In other words, should each test be written to show a positive result?
Yes, absolutely.
That would seem to make life a lot easier in one way: whenever I ran a
TestCase that was written that way, any bug would be easy to spot
because it didn't have the Green icon beside it.
Bingo. Eclipse has a button - the little red and blue squares at the top
of the JUnit pane - that hides all green tests, leaving only failed and
errored ones, which makes it even easier to spot the troublemakers.
3. Is a TestSuite just a group of TestCases that can all be invoked from
the same JUnit class?
Yes.
For instance, given a project Foo, would I typically have a single
TestSuite for Foo that, when invoked, runs the TestCases for each of the
classes that makes up Foo?
Precisely.
Although you might have several suite classes, and you might nest them.
For instance, in our current project, we have an AllTests suite (this is
quite a common name for such a thing, i think) that runs all the tests
that are meaningful on a development machine. We have some extra tests
which only make sense on the nightly build (which does some extra tasks
which take longer, and which need to be tested), so we have a
ServerBuildTests suite. We then have a super-suite
AllTestsAndServerBuildTests, which runs both of the other suites. To be
honest, those names probably aren't quite right, but hey, at least we have
the tests.
Note that Eclipse doesn't require you to have a suite to run all your
tests - in the context menu for a source folder, there's Run As > JUnit
Test, which will find all the tests in that package and run them. You
can't use this in a non-Eclipse situation (like a command-line build, such
as you might distribute, or use for a nightly build), but there are
equivalents; i have build-and-test scripts which employ the following
science:
grep -rl --include='*.java' @Test "$TEST_SRC_DIR" | cut -d / -f 2- | cut -d . -f 1 | tr / . | xargs java -ea -classpath "$TEST_CLASSES_DIR:$CLASSES_DIR" org.junit.runner.JUnitCore
Basically, I'm having a bit of confusion over exactly how much you put
in each class that extends TestCase
There's no easy answer to this. On the bright side, there are no hard and
fast rules, so you can't get it wrong. Approach the matter as you would
any other question of how much should go in a class, but don't sweat it
too much. There tends to be fairly little coupling between test methods in
a test class, so it's not a disaster if the classes that contain them are
too big or too small. Where i work, our test classes very much tend to run
to the too big end of the scale rather than the too small - i think
there's one with thirty or so test methods. That really could do with
breaking up, actually.
(sorry for the strange wording but I want to distinguish between the
class that is being tested and the class that is doing the testing and
don't know the most concise wording)
I call that a test class. The target is the 'class under test'.
and what a TestSuite should comprise.
Also, I'm very uncertain about the individual methods in each class that
extends TestCase and what sorts of results they should return. Should
all test methods result in a Green icon?
Yes, absolutely. This is essential. Fundamental.
I can see where that would be very convenient: you simply run the test
case and even if it has dozens of methods, you just scan down the list
and make sure all of them have Green icons. If they do, all your tests
just passed and you can move on to the next thing.
Not just convenient, essential.
On the other hand, I'm not completely sure how to accomplish that. I'm
picturing a method like bar() which takes two input parameters. Whenever
the values of the two input parameters are valid, bar() should produce a
valid result, which I can predict and put in a test that would look like
this:
int actualResult = myclass.bar(4);
Hey, what happened to the other parameter?
int expectedResult = 2;
assertTrue("The actual result, " + actualResult + ", does not equal the
expected result, " + expectedResult, actualResult==expectedResult);
But bar() also anticipates bad values for input parameters and throws
IllegalArgumentException for those. What would my test for that look
like? Any case I've ever written like that returns a Black icon. How
would I write a case involving an Exception so that it gives a Green
icon as the result?
Under JUnit 4:
@Test(expected=IllegalArgumentException.class)
public void testBarThrowsExceptionForInvalidParameters() {
myclass.bar(-1);
}
Under JUnit 3:
public void testBarThrowsExceptionForInvalidParameters() {
try {
myclass.bar(-1);
}
catch (IllegalArgumentException e) {
return;
}
fail(); // this will only be reached if an exception is not thrown
}
Or some variation on that - the fail() could be inside the try, right
after the call to bar, or you could have a boolean exceptionThrown =
false, then set it to true in the catch block, then do
assertTrue(exceptionThrown) at the end.
HTH.
tom
--
I need a proper outlet for my tendency towards analytical thought. --
Geneva Melzack