Upload
emery-warner
View
214
Download
0
Embed Size (px)
Citation preview
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.
@Beforepublic void setUp() { … } • Test method: a no-argument method of a TestCase class
annotated with @Test (represents a single logical test) For example:
@Testpublic 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 public void addingPositiveIntegers() { … } @Test public void addingNegativeIntegers() { … } @Test public void addingZeroes() { … } @Test public void 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
sem02-demo/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 @BeforeClasspublic 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@Testpublic 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”.