Title: Junit Architecture
1Junit Architecture
- LiuBing
- bliu76_at_yeah.net
2Agenda
- Junit Background
- Usage Junit
- Junit architecture and design pattrens
- Conclusion
3Junit
- JUnit is an open source Java testing framework
used to write and run repeatable tests. It is an
instance of the xUnit architecture for unit
testing frameworks. - JUnit features include
- Assertions for testing expected results
- Test fixtures for sharing common test data
- Test suites for easily organizing and
running tests - Graphical and textual test runners
- JUnit was originally written by Erich Gamma and
Kent Beck.
4Martin Fowler
- Never in the field of software development was so
much owed by so many to so few lines of code"
Martin Fowler
Martin Fowler??????????UML????????????XP???...??
,?????????,??ThoughtWorks??????Martin
Fowler??4?????Analysis Patterns Reusable
Object Models?UML Distilled Applying the
Standard Object Modeling Language?Refactoring
Improving the Design of Existing Code?Planning
Extreme Programming?
5JavaWorld
- 2002 JavaWorld Editors' Choice Awards (ECA)
- Best Java Performance Monitoring/Testing Tool
- 2001 JavaWorld Editors' Choice Awards (ECA)
- Best Java Performance Monitoring/Testing Tool
6Developer-Kent Beck
Kent Beck?????????????,?XP(Extreme
Programming)????,?17????????????
????????????,CRC?????????????,HotDraw???????,??xUn
it?????,??????????????????????? Kent Beck?The
Smalltalk Best Practice Patterns?Extreme
Programming Explained?Planning Extreme
Programming(?Martin Fowler??)???,?????XP??????
????Three Rivers Institute????TRI???????????????,?
??????Agile Alliance?????,Agile
Alliance??????????????????
7Developer- Erich Gamma
- Erich Gamma is the Technical Director of the
Software Technology Center of ObjectTechnologyInte
rnational (OTI) in Zurich. - Some of his recent projects are
- EclipseIde - IBM's new platform for development
tools - JFace - the Eclipse UI Framework
- Eclipse Java Tooling
- IBM VisualAge MicroEdition? IDE
- ULC - Ultra Light Client an infrastructure for
thin Java clients. - Erich pairs as often as possible with KentBeck to
work on JavaUnit. - He's author number 1 of the GangOfFour and feels
more and more guilty that there isn't a 2nd
edition...
8XP
- Extreme Programming is a discipline of
software development based on values of
simplicity, communication, feedback, and courage.
It works by bringing the whole team together in
the presence of simple practices, with enough
feedback to enable the team to see where they are
and to tune the practices to their unique
situation.
Test-Driven development
9Usage Junit
10JUnit Infected Programmers Love Writing Tests
- see Test Infected Programmers Love Writing
Tests, Java Report, July 1998, Volume 3, Number 7
11IDE
- JBuilder
- Eclipse
- Forte/Netbeans
- IntelliJ
- TogetherJ
- Visual Age
- JDeveloper Integration
12JB7
13test
- public class test
- public int add(int a,int b)
- return ab
-
14TestCase
- public class Testtest extends TestCase
- public Testtest(String s)
- super(s)
-
- protected void setUp()
- System.out.println("setUp")
-
- protected void tearDown()
- System.out.println("tearDown")
-
- public void testAdd()
- test test new test()
- this.assertEquals(12,test.add(9, 1))
-
15Junit Result
16Junit Architecture Patterns
17Goals
- What are the goals of JUnit?
18Junit Architecture -Microkernel
19Microkernel
20Patterns Generate Architectures
- The design of JUnit will be presented in a
style first used in (see "Patterns Generate
Architectures", Kent Beck and Ralph Johnson,
ECOOP 94). The idea is to explain the design of a
system by starting with nothing and applying
patterns, one after another, until you have the
architecture of the system. We will present the
architectural problem to be solved, summarize the
pattern that solves it, and then show how the
pattern was applied to JUnit
21Patterns
22Getting started- TestCase
Encapsulate a request as an object, thereby
letting you queue or log requests" Command
tells us to create an object for an operation
and give it a method "execute".
23TestCase
24Blanks to fill in- run()
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
algorithms structure
25TestCase
- Here is the template method
- public void run() Â Â Â setUp() Â Â Â runTest()
   tearDown() - The default implementations of these methods do
nothing - protected void runTest()
- protected void setUp()
- protected void tearDown()
- Since setUp and tearDown are intended to be
overridden but will be called by the framework we
declare them as protected
26SubClass TestCase
- public class Testtest extends TestCase
- public Testtest(String s)
- super(s)
-
-
- protected void setUp()
- System.out.println("setUp")
-
- protected void tearDown()
- System.out.println("tearDown")
-
-
- public void testAdd()
- test test new test()
- this.assertEquals(12,test.add(9, 1))
-
27Dont care about one or many - TestSuite
Compose objects into tree structures to represent
part-whole hierarchies. Composite lets clients
treat individual objects and compositions of
objects uniformly
28TestSuite
- public class TestSuite implements Test
- private Vector fTests new Vector(10)
- public void addTest(Test test)
- fTests.addElement(test)
-
- public void run(TestResult result)
- for (Enumeration e tests() e.hasMoreElements()
) - if (result.shouldStop() )
- break
- Test test (Test)e.nextElement()
- runTest(test, result)
-
-
29JB TestSuit
30Reporting results- TestResult
Collecting Parameter suggests that 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.
31TestResult
32TestCase Run
- public void run(TestResult result) Â Â Â
result.startTest(this) Â Â Â setUp() Â Â Â try
       runTest()       catch
(AssertionFailedError e) //1 Â Â Â Â Â Â Â
result.addFailure(this, e) Â Â Â Â Â Â catch
(Throwable e) // 2 Â Â Â Â Â Â Â result.addError(this
, e)       finally        tearDown()
  Â
33Extend TestResult
- JUnit comes with different implementations
of TestResult. The default implementation counts
the number of failures and errors and collects
the results. TextTestResult collects the results
and presents them in a textual form. Finally,
UITestResult is used by the graphical version of
the JUnit Test Runner to update the graphical
test status. TestResult is an extension point of
the framework. Clients can define their own
custom TestResult classes, for example, an
HTMLTestResult reports the results as an HTML
document.
34AssertionFailedError
- An AssertionFailedError is triggered by the
assert method provided by TestCase. JUnit
provides a set of assert methods for different
purposes. Here is the simplest one - protected void assert(boolean condition) Â Â Â
if (!condition) Â Â Â Â Â Â Â throw new
AssertionFailedError() - The AssertionFailedError is not meant to be
caught by the client (a testing method inside a
TestCase) but inside the Template Method
TestCase.run(). We therefore derive
AssertionFailedError from Error. - public class AssertionFailedError extends Error
   public AssertionFailedError () - The methods to collect the errors in TestResult
are shown below - public synchronized void addError(Test test,
Throwable t) Â Â Â fErrors.addElement(new
TestFailure(test, t)) public synchronized
void addFailure(Test test, Throwable t) Â Â Â
fFailures.addElement(new TestFailure(test, t))
35AssertionFailedError
36Observer
- ???????????????,?????????????,????????????????????
?
37TestResult
- public class TestResult extends Object
- protected Vector fFailures
- protected Vector fErrors
- protected Vector fListeners
- public synchronized void addError(Test test,
Throwable t) - fErrors.addElement(new TestFailure(test, t))
- for (Enumeration e cloneListeners().elements()
e.hasMoreElements() ) - ((TestListener)e.nextElement()).addError(test,
t) -
-
- public synchronized void addFailure(Test test,
AssertionFailedError t) - fFailures.addElement(new TestFailure(test, t))
- for (Enumeration e cloneListeners().elements()
e.hasMoreElements() ) - ((TestListener)e.nextElement()).addFailure(test
, t) -
-
- public synchronized void addListener(TestListener
listener) - fListeners.addElement(listener)
-
38TestListener
- public interface TestListener
- /
- An error occurred.
- /
- public void addError(Test test, Throwable t)
- /
- A failure occurred.
- /
- public void addFailure(Test test,
AssertionFailedError t) - /
- A test ended.
- /
- public void endTest(Test test)
- /
- A test started.
- /
- public void startTest(Test test)
39TestRunner
- public class TestRunner extends BaseTestRunner
- public synchronized void addError(Test test,
Throwable t) - writer().print("E")
-
-
- public synchronized void addFailure(Test
test, AssertionFailedError t) - writer().print("F")
-
- public synchronized void startTest(Test test)
- writer().print(".")
- if (fColumn gt 40)
- writer().println()
- fColumn 0
-
-
- public void endTest(Test test)
-
40Adapter
Convert the interface of a class into another
interface clients expect
41Code
- public class TestMoneyEquals extends MoneyTest
   public TestMoneyEquals() super("testMoneyEqu
als") Â - Â Â protected void runTest ()
testMoneyEquals() -
- TestCase test new MoneyTest("testMoneyEquals ")
   protected void runTest()
testMoneyEquals() -
42Decorator
??????????????????
43TestDecorator
- public class TestDecorator extends Assert
implements Test - protected Test fTest
- public TestDecorator(Test test)
- fTest test
-
- /
- The basic run behaviour.
- /
- public void basicRun(TestResult result)
- fTest.run(result)
-
- public int countTestCases()
- return fTest.countTestCases()
-
- public void run(TestResult result)
- basicRun(result)
-
44TestSetup
- public class TestSetup extends TestDecorator
- public TestSetup(Test test)
- super(test)
-
- public void run(final TestResult result)
- Protectable p new Protectable()
- public void protect() throws Exception
- setUp()
- basicRun(result)
- tearDown()
-
-
- result.runProtected(this, p)
-
45Summary
- Command
- Template method
- Collecting Parameter
- Adapter
- Pluggables Selector
- Composite
- Observer
- Decorator
- MicroKernel (SA Pattern)
46Conclusion
47Patterns
- We found discussing the design in terms of
patterns to be invaluable, both as we were
developing the framework and as we try to explain
it to others. You are now in a perfect position
to judge whether describing a framework with
patterns is effective. If you liked the
discussion above, try the same style of
presentation for your own system.
48Pattern density
- There is a high pattern "density" around
TestCase, which is the key abstraction of JUnit.
Designs with high pattern density are easier to
use but harder to change. We have found that such
a high pattern density around key abstractions is
common for mature frameworks. The opposite should
be true of immature frameworks - they should have
low pattern density. Once you discover what
problem you are really solving, then you can
begin to "compress" the solution, leading to a
denser and denser field of patterns where they
provide leverage.
49Eat your own dog food
- As soon as we had the base unit testing
functionality implemented, we applied it
ourselves. A TestTest verifies that the framework
reports the correct results for errors,
successes, and failures. We found this invaluable
as we continued to evolve the design of the
framework. We found that the most challenging
application of JUnit was testing its own
behavior.
50Intersection, not union
- There is a temptation in framework
development to include every feature you can.
After all, you want to make the framework as
valuable as possible. However, there is a
counteracting force- developers have to decide to
use your framework. The fewer features the
framework has, the easier it is to learn, the
more likely a developer will use it. JUnit is
written in this style. It implements only those
features absolutely essential to running tests-
running suites of tests, isolating the execution
of tests from each other, and running tests
automatically. Sure, we couldnt resist adding
some features but we were careful to put them
into their own extensions package
(test.extensions). A notable member of this
package is a TestDecorator allowing execution of
additional code before and after a test.
51Framework writers read their code
- We spent far more time reading the JUnit code
than we spent writing it, and nearly as much time
removing duplicate functionality as we spent
adding new functionality. We experimented
aggressively with the design, adding new classes
and moving responsibility around in as many
different ways as we could imagine. We were
rewarded (and are still being rewarded) for our
monomania by a continuous flow of insights into
JUnit, testing, object design, framework
development, and opportunities for further
articles.