Unit Testing Theory
Unit Testing Theory
Unit Testing
Thanks to Nick Parlante for much of this handout
This handout introduces the ideas of unit testing and looks at using JUnit in Eclipse.
Unit Testing
• For each piece of "work" code -- a class or a method, pair the work code with some "unit test" code
• The unit test code calls the work code through its public API, calling it a few different ways and checking
the results.
• Test code does not need to be exhaustive -- test code adds a lot of value even just hitting a few fairly
obvious cases.
• Unit tests are a standard, maintained way to keep tests in parallel with the work code
• vs. the more one-off, informal way of doing some testing here and there manually
• The unit tests are more of an investment -- effort to build, but they improve development for the lifetime
of the code.
• At first the tests fail, since the work code is not written -- unit tests can fail by not even compiling -- that's
fine at this stage
• Now start writing the work code, eventually the tests pass, yay!
• Don't get caught up trying to write a unit test for every possible angle -- just hit a few good ones. Tests do
not need to try every possible input -- just a few. Put another way, unit tests have diminishing returns.
The first few tests add the most value.
Evolve Trick
• This is just one technique to get started. I start with the code for my first test.
• Then the later tests, I copy/paste the first test, and then adjust the inputs/expected-output to try to push on
the code in a slightly different way.
• In this way, the series of tests "evolve", each being a variation on the one above.
• The disadvantage here can be that the tests are too similar, but it's a convenient way to get started.
Strangely Compelling
• The cycle of working at the code until tests pass is strangely enjoyable. You hit the run button, and it's a
little charge when the tests finally run green. I think this is because you get a concrete sense of progress
vs. the old style of working on code but not really knowing if you are getting anywhere.
• You can write obnoxious, hard tests that hit at edge cases. If the code can get past these, it can do
anything!
• Or put another way, you want take a sort of aggressive posture towards the code in the tests -- pushing on
the code in ways that will make its author nervous.
• The tests do not need to be exhaustive -- the space of inputs is so big, exhaustiveness is a false goal. Try
to hit a meaningful variety of test cases, and that's good enough and don't worry about it. Unit tests
have diminishing returns. Once you have a few good ones, you've got most of the benefit.
Running JUnit
• At the left, select the test class, right click, select Run JUnit Test
• Click on the JUnit tab in the left pane to see the results
• Alternately, click on the package on the left. Then running JUnit Tests will run all of the tests.
• The stack trace at the lower left shows the call sequence. If there was an assert failure, it shows the
expected and actual values. (very handy!) Double click it to get details of the expected and actual
values.
• Double clicking in the stack trace goes to that line.
• The data and variables are not live -- the JUnit report is of what happened in the past
• To see the data live, put a breakpoint in the work code, and select Debug... to run the unit test in the
debugger where you can look at the values as it runs.
• Key idea: In the list of tests in the JUnit pane in Eclipse, you can right-click one test to Run or Debug just
that test. In this way, you can break in the work code for just the test that is breaking.
Emails Example
import java.util.*;
/*
* Emails Class -- unit testing example.
* Encapsulates some text with email addresses in it.
* getUsers() returns a list of the usernames from the text.
*/
public class Emails {
private String text;
while (true) {
int at = text.indexOf('@', pos);
if (at == -1) break;
return users;
}
EmailsTest
import junit.framework.TestCase;
import java.util.*;
/*
EmailsTest -- unit tests for the Emails class.
*/
public class EmailsTest extends TestCase {
// Basic use
public void testUsersBasic() {
Emails emails = new Emails("foo [email protected] xyz [email protected] baz");
assertEquals(Arrays.asList("bart", "marge"), emails.getUsers() );
// Note: Arrays.asList(...) is a handy way to make list literal.
// Also note that .equals() works for collections, so the above works.
}
// No emails
emails = new Emails("no emails here!");
assertEquals(Collections.emptyList(), emails.getUsers());
// Empty string
emails = new Emails("");
assertEquals(Collections.emptyList(), emails.getUsers());
}
}