Transcript JUnit_intro

JUnit intro
Kalvis Apsitis
What are “Programmer Tests”?
• Programmer Testing is the testing
performed by a developer with the goal of
verifying the correct functioning of his/her
own code
• Programmer Tests are automated unit
tests that exercise program units such as
classes and methods , not the complete
features or requirements supported by the
software as a whole
• Important distinction: if it’s not automated, it’s not
really programmer testing – that’s exploratory
testing.
Safety Net
• Having a good test coverage with an
automated unit test harness…
– prevents a system from becoming legacy(*),
and
– enables effective refactoring, because it also
prevents regression
• Metaphor: Mold
• Michael Feathers defines “legacy code”
as code without tests
Michael Feathers (2004)
• I don't care how good you think your design is. If
I can't walk in and write a test for an arbitrary
method of yours in five minutes its not as good
as you think it is, and whether you know it or not,
you're paying a price for it:
– You think your design is good? Pick a class, any
class, and try to instantiate it in a test harness.
– Can you make this class work outside the
application?
– Can you get it to the point where you can tinker with
it, in real time, build it alone in less than a second,
and add tests to find out what it really does in certain
situations.
Available tools for Java
• JUnit (www.junit.org)
– The de facto Java unit testing framework
– Extremely simple, suitable for unit/component testing
– Plenty of extensions for J2EE, for example
• TestNG (testng.org)
– Very close to JUnit but not quite
– Can configure test groups/suites in a more flexible
way (it is usable for some integration testing)
JUnit Concepts
• Test suite: Executable row of several Test cases
• Test case: Java class containing test methods
• Test fixture: the initial state of a Test Case, consisting of the
member variables initialized through a method annotated with
@Before. E.g.
@Before
public void setUp() { … }
• Test method: a no-argument method of a TestCase class
annotated with @Test (represents a single logical test) For
example:
@Test
public void someMethod() { … }
============
Only things to do in “@Test testA(){...}” methods is
business logic + assertions.
Test fixture contains everything else needed to run the
tests (it is initialized in "@Before setUp(){...}").
TestCase lifecycle
@Before public void setUp() {...}
gets called once before each test method is executed
@After public void tearDown() {...}
gets called once after each test method has been
executed – regardless of whether the test passed or
failed (or threw some other exception).
• Each test method gets called exactly once in some order
(test methods should be isolated, so the order does not
matter).
• The same instance of a TestCase may or may not be
used for executing the tests – don’t depend on the
constructor, use setUp() for setup!
Suite Example
import org.junit.*;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses(value =
{TestCase1.class, TestCase2.class})
public class SomeSuite {
// any code;
// may contain TestCase1, TestCase2
// as inner classes, if their fixtures
have much
// in common
}
Simple Annotations in a
TestCase
import org.junit.*;
public class TestCase1 {
@Before protected void setUp() {super.setUp();}
@After protected void tearDown() {super.tearDown();}
@Test public void methodOne() { ... }
@Test(expected = RuntimeException.class)
public void testSomeException() { ... }
@Ignore(value = "Test method not ready")
@Test
public void ignoredTestCase() { ... }
}
Simple TestCase (3)
import org.junit.*;
public class TestCalculator {
private Calculator calculator;
protected void setUp() {
super.setUp();
calculator = new Calculator();
}
@Test
public void addingPositiveIntegers() {
int expected = 5;
int actual = calculator.add(2, 3);
assertEquals("2 + 3 should be 5", expected,
actual);
}
}
Test anything that could fail
import org.junit.*;
public class TestCalculator {
private Calculator calculator;
@Before
protected void setUp() { … }
@After
protected void tearDown() { … }
@Test
@Test
@Test
@Test
}
public
public
public
public
void
void
void
void
addingPositiveIntegers() { … }
addingNegativeIntegers() { … }
addingZeroes() { … }
addingPositiveToNegative() { … }
How many method calls are executed?
Test Granularity
• Each unit test should check one specific piece of
functionality. Do not combine multiple, unrelated
tests into a single testXXX( ) method.
• If the first assertEquals( ) in some method fails,
the remainder is not executed. You won't know if
the other (unrelated) asserts are functional.
• One method may contain several asserts, if they
are related, i.e. if the failure of one assert would
cause failures in other asserts anyway.
Exercise 1:
Write a TestCase
• Task: Write a JUnit TestCase for the
Template class, place it under
sem02demo/src/main/java/exercise1/TemplateTest.java
• There’s a bug in Template class. Try to
find it!
Simple TestSuite
import org.junit.*;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses(value =
{CalculatorIntegerTest.class,
CalculatorFloatingPointTest.class,
CalculatorLogarithmsTest.class })
public class CalculatorTestSuite {
// Can leave empty
}
Exercise 2:
Write a TestSuite
• Task:
– Write a JUnit TestSuite exercise2.AllTests
that collects all the TestCase classes from
sem02_demo/src/test/java/exercise2.
What if I need to do something
expensive in setUp()?
• If you need to perform some kind of one-time setup
before running the test methods of a TestCase (and
can’t/won’t replace the expensive stuff with a mock
implementation), use @BeforeClass annotation:
public class Example {
@BeforeClass
public static void onlyOnce() { }
@Before
public void beforeEachTest() {}
@Test public void one() { ... }
@Test public void two() { ... }
}
Example of @BeforeClass
public class InterestCalculatorTestWithTestSetup {
private static double interestRate, loanAmount;
private static int loanDuration;
public static Test suite() {
TestSuite suite = new TestSuite(InterestCalculatorTestWithTestSetup.class);
TestSetup wrapper = new TestSetup(suite) {
public void setUp() throws IOException {
ResourceBundle bundle = ResourceBundler.getBundle(
InterestCalculatorTestWithTestSetup.class.getName());
inrerestRate = Double.parseDouble(bundle.getString(“interest.rate”));
loanAmount = Double.parseDouble(bundle.getString(“loan.amount”));
loanDuration = Interest.parseInt(bundle.getString(“loan.duration”));
}
};
return wrapper;
}
public void testInterestCalculation() { … }
}
Assertions
• We already saw one assertion in action, the
assertEquals() method
• There are a bunch others – take a look at the
Javadocs for org.junit.Assert
• The most often used assertions –assertEquals(),
assertNull(), assertSame(), assertTrue() and
their opposites – are enough for most situations
and the rest you can easily write on your own.
• Assert.fail() is used, if control should not reach
that line in the test - this makes the test method
to fail.
Testing Expected Exceptions
@Test
public void method1() {
try {
Template t = new Template( "My ${f\\oo
template" );
fail("Should NOT allow unterminated variables
in " + t);
}
catch (IllegalArgumentException expected) {}
}
@Test(expected=IllegalArgumentException.class)
public void method2() {
new Template( "This is my ${f\\oo template" );
}
First approach is longer, but allows more precise
control, where exactly the test method fails.
What happens when an
assertion fails?
• The failed assertions create a
org.junit.runner.notification.Failure when
they fail.
• JUnit tags the test as “failure” for later
reference.
• If a test throws any other exception (e.g.
NullPointerException), JUnit again catches
these but tags the test as “error” instead of
“failure”.