JUnit The framework. Goal of the presentation showing the design and construction of JUnit, a piece...
-
Upload
clement-charles -
Category
Documents
-
view
228 -
download
0
Transcript of JUnit The framework. Goal of the presentation showing the design and construction of JUnit, a piece...
Goal of the presentation
showing the design and construction of JUnit,
a piece of software with proven value
Goals of JUnit to write a framework
– within which developers will actually write tests
The framework has to
– use familiar tools, so there is little new to learn.
– require no more work than absolutely necessary to write a new test.
– eliminate duplicated effort.
Goals of JUnit Creation of tests that retain their value over time.
– Someone other than the original author has to be able • to execute the tests • and interpret the results.
– It should be possible to • combine tests from various authors • and run them together without fear of interference
leverage existing tests to create new ones.
– Creating a setup or fixture is expensive and a framework has to
• enable reusing fixtures to run different tests
TestCase applies Command
public abstract class TestCase implements Test {public abstract void run();
}
Issues– an object (TestCase) to represent the basic concept
– creating tests that retain their value over time
– making test writing more inviting • object developers are used to developing with objects
TestCase applies Command
public abstract class TestCase implements Test {public abstract void run();
}
Command Pattern– "Encapsulate a request as an object,
thereby letting you… queue or log requests…"
– create an object for an operation – give the object a method "execute".
TestCase.run() applies Template Method
Issues
– giving the developer a convenient "place" to put • their fixture code • and their test code
– common structure to all tests• set up a test fixture, • run some code against the fixture,• check some results, • clean up the fixture
TestCase.run() applies Template Method
Template Method– "Define the skeleton of an algorithm in an operation, deferring some
steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure."
public void run() { setUp(); runTest(); tearDown(); }
// default implementations of these methods do nothing: protected void runTest() { } protected void setUp() { } protected void tearDown() { }
TestResult applies Collecting Parameter
Issues– After the test has run,
need for a record, a summary of what did and didn’t work
Collecting Parameter
– when you need to collect results over several methods, you should add a parameter to the method and pass an object that will collect the results for you.
– TestResult : Reporting results
Failures and errors Failure
– anticipated and checked for with assertions. – signaled with an AssertionFailedError exception
Errors – unanticipated problems like an ArrayIndexOutOfBoundsException.
public void run(TestResult result) { result.startTest(this); setUp(); try { runTest(); } catch (AssertionFailedError e) {result.addFailure(this, e); } catch (Throwable e) {result.addError(this, e); } finally { tearDown(); } }
Failures and errors
The canonical form of collecting parameter requires us to pass the collecting parameter to each method.
– each of the testing methods would require a parameter for the TestResult.
– a "pollution" of these method signatures.
– as a benevolent side effect of using exceptions to signal failures we can avoid this signature pollution.
TestCase applies either Adapter with an anonymous inner class or Pluggable Selector
Issues– To avoid the unnecessary proliferation of classes
• All test cases are implemented as different methods in the same class. • A given test case class may implement many different methods,
each defining a single test case. • Each test case has a descriptive name
like testMoneyEquals or testMoneyAdd.
– The test cases don’t conform to a simple command interface. • Different instances of the same Command class need to be invoked
with different methods.
– The problem is to make all the test cases look the same from the point of view of the invoker of the test
TestCase applies either Adapter with an anonymous inner class or Pluggable Selector
Adapter– "Convert the interface of a class into another interface clients expect".
– a class adapter, which uses subclassing to adapt the interface • implement a subclass for each test case
// explicit subclasspublic class TestMoneyEquals extends MoneyTest { public testMoneyEquals() {
super("testMoneyEquals"); }
protected void runTest () { testMoneyEquals(); } }
//anonymous inner classTestCase test= new MoneyTest("testMoneyEquals ") {
protected void runTest() { testMoneyEquals(); } };
TestCase applies either Adapter with an anonymous inner class or Pluggable Selector
PluggableSelector– use a single class which can be parameterized to perform different logic without requiring
subclassing • stores a method selector in an instance variable • the Java reflection API allows us to invoke a method from a string representing the method’s
name
• the pluggable selector is the default implementation of the runTest method.
protected void runTest() throws Throwable { Method runMethod= null; try { runMethod= getClass().getMethod( fName,
new Class[0]); } catch (NoSuchMethodException e) { assert("Method \""+fName+"\" not found", false); } try { runMethod.invoke(this, new Class[0]); } // catch InvocationTargetException and IllegalAccessException }
TestSuite applies Composite
Issues– to get confidence in the state of a system
need to run many tests
– to support suites of suites of suites of tests
Structural integrity tests : - this type of test is usually performed on a single unit of the software at a time - test procedures written to exercise all the important elements of the code- this may include exercising all the paths in the module
TestSuite applies Composite
Composite– "Compose objects into tree structures to represent part-whole hierarchies.
Composite lets clients treat individual objects and compositions of objects uniformly."
Participants– Component: declares the interface to use to interact with tests. – Composite: implements this interface and maintains a collection of tests. – Leaf: represents a test case in a composition that conforms to the
Component interface.
TestSuite applies Composite
Participants– Component: declares the interface to use to interact with tests.
public interface Test { public abstract void run(TestResult result); }
TestSuite applies Composite
Participants– Composite: implements this interface and maintains a collection of tests.
public class TestSuite implements Test { private Vector fTests= new Vector(); //children
// delegate to childrenpublic void run(TestResult result) {
for (Enumeration e= fTests.elements(); e.hasMoreElements(); ){ Test test= (Test)e.nextElement(); test.run(result); } }
// add test to a test suitepublic void addTest(Test test) {
fTests.addElement(test); }
}
TestSuite applies Composite
Participants– Leaf: represents a test case in a composition that conforms to the Component
interface.
// JUnit v 3.2public abstract class TestCase implements Test {
public void run(TestResult result) {result.run(this);}
public void runBare() throws Throwable {setUp();try { runTest();}finally {tearDown();}
}
Creating a test suite Static specification of a test suite
public static Test suite() { TestSuite suite= new TestSuite(); suite.addTest(new MoneyTest("testMoneyEquals")); suite.addTest(new MoneyTest("testSimpleAdd")); }
Dynamic extraction of the test methods and creation of a suite containing them.
– convention • methods that start with the prefix "test" • take no arguments.
– constructing the test objects by using reflection.
public static Test suite() { return new TestSuite(MoneyTest.class); }
/*** Constructs a TestSuite from the given class. * Adds all the methods starting with "test" as test cases. */public TestSuite(final Class theClass) {
fName= theClass.getName();
Constructor constructor= getConstructor(theClass);if (!Modifier.isPublic(theClass.getModifiers())) { addTest(warning("Class "+theClass.getName()+" is not public")); return;}if (constructor == null) { addTest(warning("Class "+theClass.getName()
+" has no public constructor TestCase(String name)"));return;}
Class superClass= theClass;Vector names= new Vector();while (Test.class.isAssignableFrom(superClass)) {
Method[] methods= superClass.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { addTestMethod(methods[i], names, constructor);} superClass= superClass.getSuperclass();}
if (fTests.size() == 0) addTest(warning("No tests found in "+theClass.getName()));}
References
Erich Gamma, Kent Beck, JUnit A Cook's Tour– http://junit.sourceforge.net/doc/cookstour/cookstour.htm
JUnit Primer– http://www.clarkware.com/articles/JUnitPrimer.html
Refactorisation de TestCase… Failure
– anticipated and checked for with assertions. – signaled with an AssertionFailedError error
Errors – unanticipated problems like an ArrayIndexOutOfBoundsException.
public void run(TestResult result) { result.startTest(this); setUp(); try { runTest(); } catch (AssertionFailedError e) {result.addFailure(this, e); } catch (Throwable e) {result.addError(this, e); } finally { tearDown(); } }