540 lines
28 KiB
HTML
540 lines
28 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd" xml:lang="en">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
|
<meta name="Author" content="Dean Herington"/>
|
|
<meta name="KeyWords" content="HUnit, unit testing, test-first development, Haskell, JUnit"/>
|
|
<title>HUnit 1.0 User's Guide</title>
|
|
</head>
|
|
<body>
|
|
|
|
<h1>HUnit 1.2 User's Guide</h1>
|
|
|
|
<p>HUnit is a unit testing framework for Haskell, inspired by the JUnit tool for Java. This
|
|
guide describes how to use HUnit, assuming you are familiar with Haskell, though not
|
|
necessarily with JUnit. You can obtain HUnit, including this guide, at <a
|
|
href="http://code.haskell.org/HUnit">http://code.haskell.org/HUnit</a>.</p>
|
|
|
|
<h2>Introduction</h2>
|
|
|
|
<p>A test-centered methodology for software development is most effective when tests are
|
|
easy to create, change, and execute. The <a href="http://www.junit.org">JUnit</a> tool
|
|
pioneered support for test-first development in <a href="http://java.sun.com">Java</a>.
|
|
HUnit is an adaptation of JUnit to Haskell, a general-purpose, purely functional
|
|
programming language. (To learn more about Haskell, see <a href="http://www.haskell.org"
|
|
>http://www.haskell.org</a>.)</p>
|
|
|
|
<p>With HUnit, as with JUnit, you can easily create tests, name them, group them into
|
|
suites, and execute them, with the framework checking the results automatically. Test
|
|
specification in HUnit is even more concise and flexible than in JUnit, thanks to the
|
|
nature of the Haskell language. HUnit currently includes only a text-based test
|
|
controller, but the framework is designed for easy extension. (Would anyone care to
|
|
write a graphical test controller for HUnit?)</p>
|
|
|
|
<p>The next section helps you get started using HUnit in simple ways. Subsequent sections
|
|
give details on <a href="#WritingTests">writing tests</a> and <a href="#RunningTests"
|
|
>running tests</a>. The document concludes with a section describing HUnit's <a
|
|
href="#ConstituentFiles">constituent files</a> and a section giving <a
|
|
href="#References">references</a> to further information.</p>
|
|
|
|
<h2 id="GettingStarted">Getting Started</h2>
|
|
|
|
<p>In the Haskell module where your tests will reside, import module <tt>Test.HUnit</tt>:</p>
|
|
<pre>
|
|
import Test.HUnit
|
|
</pre>
|
|
<p>Define test cases as appropriate:</p>
|
|
<pre>
|
|
test1 = TestCase (assertEqual "for (foo 3)," (1,2) (foo 3))
|
|
test2 = TestCase (do (x,y) <- partA 3
|
|
assertEqual "for the first result of partA," 5 x
|
|
b <- partB y
|
|
assertBool ("(partB " ++ show y ++ ") failed") b)
|
|
</pre>
|
|
<p>Name the test cases and group them together:</p>
|
|
<pre>
|
|
tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2]
|
|
</pre>
|
|
<p>Run the tests as a group. At a Haskell interpreter prompt, apply the function
|
|
<tt>runTestTT</tt> to the collected tests. (The "<tt>TT</tt>" suggests
|
|
<strong>T</strong>ext orientation with output to the <strong>T</strong>erminal.)</p>
|
|
<pre>
|
|
> runTestTT tests
|
|
Cases: 2 Tried: 2 Errors: 0 Failures: 0
|
|
>
|
|
</pre>
|
|
<p>If the tests are proving their worth, you might see:</p>
|
|
<pre>
|
|
> runTestTT tests
|
|
### Failure in: 0:test1
|
|
for (foo 3),
|
|
expected: (1,2)
|
|
but got: (1,3)
|
|
Cases: 2 Tried: 2 Errors: 0 Failures: 1
|
|
>
|
|
</pre>
|
|
<p>Isn't that easy?</p>
|
|
|
|
<p>You can specify tests even more succinctly using operators and overloaded functions that
|
|
HUnit provides:</p>
|
|
<pre>
|
|
tests = test [ "test1" ~: "(foo 3)" ~: (1,2) ~=? (foo 3),
|
|
"test2" ~: do (x, y) <- partA 3
|
|
assertEqual "for the first result of partA," 5 x
|
|
partB y @? "(partB " ++ show y ++ ") failed" ]
|
|
</pre>
|
|
<p>Assuming the same test failures as before, you would see:</p>
|
|
<pre>
|
|
> runTestTT tests
|
|
### Failure in: 0:test1:(foo 3)
|
|
expected: (1,2)
|
|
but got: (1,3)
|
|
Cases: 2 Tried: 2 Errors: 0 Failures: 1
|
|
>
|
|
</pre>
|
|
|
|
<h2 id="WritingTests">Writing Tests</h2>
|
|
|
|
<p>Tests are specified compositionally. <a href="#Assertions">Assertions</a> are combined to
|
|
make a <a href="#TestCase">test case</a>, and test cases are combined into <a
|
|
href="#Tests">tests</a>. HUnit also provides <a href="#AdvancedFeatures">advanced
|
|
features</a> for more convenient test specification.</p>
|
|
|
|
<h3 id="Assertions">Assertions</h3>
|
|
|
|
<p>The basic building block of a test is an <b>assertion</b>.</p>
|
|
<pre>
|
|
type Assertion = IO ()
|
|
</pre>
|
|
<p>An assertion is an <tt>IO</tt> computation that always produces a void result. Why is an
|
|
assertion an <tt>IO</tt> computation? So that programs with real-world side effects can
|
|
be tested. How does an assertion assert anything if it produces no useful result? The
|
|
answer is that an assertion can signal failure by calling <tt>assertFailure</tt>.</p>
|
|
<pre>
|
|
assertFailure :: String -> Assertion
|
|
assertFailure msg = ioError (userError ("HUnit:" ++ msg))
|
|
</pre>
|
|
<p><tt>(assertFailure msg)</tt> raises an exception. The string argument identifies the
|
|
failure. The failure message is prefixed by "<tt>HUnit:</tt>" to mark it as an HUnit
|
|
assertion failure message. The HUnit test framework interprets such an exception as
|
|
indicating failure of the test whose execution raised the exception. (Note: The details
|
|
concerning the implementation of <tt>assertFailure</tt> are subject to change and should
|
|
not be relied upon.)</p>
|
|
|
|
<p><tt>assertFailure</tt> can be used directly, but it is much more common to use it
|
|
indirectly through other assertion functions that conditionally assert failure.</p>
|
|
<pre>
|
|
assertBool :: String -> Bool -> Assertion
|
|
assertBool msg b = unless b (assertFailure msg)
|
|
|
|
assertString :: String -> Assertion
|
|
assertString s = unless (null s) (assertFailure s)
|
|
|
|
assertEqual :: (Eq a, Show a) => String -> a -> a -> Assertion
|
|
assertEqual preface expected actual =
|
|
unless (actual == expected) (assertFailure msg)
|
|
where msg = (if null preface then "" else preface ++ "\n") ++
|
|
"expected: " ++ show expected ++ "\n but got: " ++ show actual
|
|
</pre>
|
|
<p>With <tt>assertBool</tt> you give the assertion condition and failure message separately.
|
|
With <tt>assertString</tt> the two are combined. With <tt>assertEqual</tt> you provide a
|
|
"preface", an expected value, and an actual value; the failure message shows the two
|
|
unequal values and is prefixed by the preface. Additional ways to create assertions are
|
|
described later under <a href="#AdvancedFeatures">Advanced Features</a>.</p>
|
|
|
|
<p>Since assertions are <tt>IO</tt> computations, they may be combined--along with other
|
|
<tt>IO</tt> computations--using <tt>(>>=)</tt>, <tt>(>>)</tt>, and the <tt>do</tt>
|
|
notation. As long as its result is of type <tt>(IO ())</tt>, such a combination
|
|
constitutes a single, collective assertion, incorporating any number of constituent
|
|
assertions. The important features of such a collective assertion are that it fails if
|
|
any of its constituent assertions is executed and fails, and that the first constituent
|
|
assertion to fail terminates execution of the collective assertion. Such behavior is
|
|
essential to specifying a test case.</p>
|
|
|
|
<h3 id="TestCase">Test Case</h3>
|
|
|
|
<p>A <b>test case</b> is the unit of test execution. That is, distinct test cases are
|
|
executed independently. The failure of one is independent of the failure of any other.</p>
|
|
|
|
<p>A test case consists of a single, possibly collective, assertion. The possibly multiple
|
|
constituent assertions in a test case's collective assertion are <b>not</b> independent.
|
|
Their interdependence may be crucial to specifying correct operation for a test. A test
|
|
case may involve a series of steps, each concluding in an assertion, where each step
|
|
must succeed in order for the test case to continue. As another example, a test may
|
|
require some "set up" to be performed that must be undone ("torn down" in JUnit
|
|
parlance) once the test is complete. In this case, you could use Haskell's
|
|
<tt>IO.bracket</tt> function to achieve the desired effect.</p>
|
|
|
|
<p>You can make a test case from an assertion by applying the <tt>TestCase</tt> constructor.
|
|
For example, <tt>(TestCase (return ()))</tt> is a test case that never
|
|
fails, and
|
|
<tt>(TestCase (assertEqual "for x," 3 x))</tt>
|
|
is a test case that checks that the value of <tt>x</tt> is 3. Additional ways
|
|
to create test cases are described later under <a href="#AdvancedFeatures">Advanced
|
|
Features</a>.</p>
|
|
|
|
<h3 id="Tests">Tests</h3>
|
|
|
|
<p>As soon as you have more than one test, you'll want to name them to tell them apart. As
|
|
soon as you have more than several tests, you'll want to group them to process them more
|
|
easily. So, naming and grouping are the two keys to managing collections of tests.</p>
|
|
|
|
<p>In tune with the "composite" design pattern [<a href="#DesignPatterns">1</a>], a
|
|
<b>test</b> is defined as a package of test cases. Concretely, a test is either a single
|
|
test case, a group of tests, or either of the first two identified by a label.</p>
|
|
<pre>
|
|
data Test = TestCase Assertion
|
|
| TestList [Test]
|
|
| TestLabel String Test
|
|
</pre>
|
|
<p>There are three important features of this definition to note:</p>
|
|
<ul>
|
|
<li>A <tt>TestList</tt> consists of a list of tests rather than a list of test cases.
|
|
This means that the structure of a <tt>Test</tt> is actually a tree. Using a
|
|
hierarchy helps organize tests just as it helps organize files in a file system.</li>
|
|
<li>A <tt>TestLabel</tt> is attached to a test rather than to a test case. This means
|
|
that all nodes in the test tree, not just test case (leaf) nodes, can be labeled.
|
|
Hierarchical naming helps organize tests just as it helps organize files in a file
|
|
system.</li>
|
|
<li>A <tt>TestLabel</tt> is separate from both <tt>TestCase</tt> and <tt>TestList</tt>.
|
|
This means that labeling is optional everywhere in the tree. Why is this a good
|
|
thing? Because of the hierarchical structure of a test, each constituent test case
|
|
is uniquely identified by its path in the tree, ignoring all labels. Sometimes a
|
|
test case's path (or perhaps its subpath below a certain node) is a perfectly
|
|
adequate "name" for the test case (perhaps relative to a certain node). In this
|
|
case, creating a label for the test case is both unnecessary and inconvenient.</li>
|
|
</ul>
|
|
<p>The number of test cases that a test comprises can be computed with
|
|
<tt>testCaseCount</tt>.</p>
|
|
<pre>
|
|
testCaseCount :: Test -> Int
|
|
</pre>
|
|
<p>As mentioned above, a test is identified by its <b>path</b> in the test hierarchy.</p>
|
|
<pre>
|
|
data Node = ListItem Int | Label String
|
|
deriving (Eq, Show, Read)
|
|
|
|
type Path = [Node] -- Node order is from test case to root.
|
|
</pre>
|
|
<p>Each occurrence of <tt>TestList</tt> gives rise to a <tt>ListItem</tt> and each
|
|
occurrence of <tt>TestLabel</tt> gives rise to a <tt>Label</tt>. The <tt>ListItem</tt>s
|
|
by themselves ensure uniqueness among test case paths, while the <tt>Label</tt>s allow
|
|
you to add mnemonic names for individual test cases and collections of them.</p>
|
|
|
|
<p>Note that the order of nodes in a path is reversed from what you might expect: The first
|
|
node in the list is the one deepest in the tree. This order is a concession to
|
|
efficiency: It allows common path prefixes to be shared.</p>
|
|
|
|
<p>The paths of the test cases that a test comprises can be computed with
|
|
<tt>testCasePaths</tt>. The paths are listed in the order in which the corresponding
|
|
test cases would be executed.</p>
|
|
<pre>
|
|
testCasePaths :: Test -> [Path]
|
|
</pre>
|
|
|
|
<p>The three variants of <tt>Test</tt> can be constructed simply by applying
|
|
<tt>TestCase</tt>, <tt>TestList</tt>, and <tt>TestLabel</tt> to appropriate arguments.
|
|
Additional ways to create tests are described later under <a href="#AdvancedFeatures"
|
|
>Advanced Features</a>.</p>
|
|
|
|
<p>The design of the type <tt>Test</tt> provides great conciseness, flexibility, and
|
|
convenience in specifying tests. Moreover, the nature of Haskell significantly augments
|
|
these qualities:</p>
|
|
<ul>
|
|
<li>Combining assertions and other code to construct test cases is easy with the
|
|
<tt>IO</tt> monad.</li>
|
|
<li>Using overloaded functions and special operators (see below), specification of
|
|
assertions and tests is extremely compact.</li>
|
|
<li>Structuring a test tree by value, rather than by name as in JUnit, provides for more
|
|
convenient, flexible, and robust test suite specification. In particular, a test
|
|
suite can more easily be computed "on the fly" than in other test frameworks.</li>
|
|
<li>Haskell's powerful abstraction facilities provide unmatched support for test
|
|
refactoring.</li>
|
|
</ul>
|
|
|
|
<h3 id="AdvancedFeatures">Advanced Features</h3>
|
|
|
|
<p>HUnit provides additional features for specifying assertions and tests more conveniently
|
|
and concisely. These facilities make use of Haskell type classes.</p>
|
|
|
|
<p>The following operators can be used to construct assertions.</p>
|
|
<pre>
|
|
infix 1 @?, @=?, @?=
|
|
|
|
(@?) :: (AssertionPredicable t) => t -> String -> Assertion
|
|
pred @? msg = assertionPredicate pred >>= assertBool msg
|
|
|
|
(@=?) :: (Eq a, Show a) => a -> a -> Assertion
|
|
expected @=? actual = assertEqual "" expected actual
|
|
|
|
(@?=) :: (Eq a, Show a) => a -> a -> Assertion
|
|
actual @?= expected = assertEqual "" expected actual
|
|
</pre>
|
|
<p>You provide a boolean condition and failure message separately to <tt>(@?)</tt>, as for
|
|
<tt>assertBool</tt>, but in a different order. The <tt>(@=?)</tt> and <tt>(@?=)</tt>
|
|
operators provide shorthands for <tt>assertEqual</tt> when no preface is required. They
|
|
differ only in the order in which the expected and actual values are provided. (The
|
|
actual value--the uncertain one--goes on the "?" side of the operator.)</p>
|
|
|
|
<p>The <tt>(@?)</tt> operator's first argument is something from which an assertion
|
|
predicate can be made, that is, its type must be <tt>AssertionPredicable</tt>.</p>
|
|
<pre>
|
|
type AssertionPredicate = IO Bool
|
|
|
|
class AssertionPredicable t
|
|
where assertionPredicate :: t -> AssertionPredicate
|
|
|
|
instance AssertionPredicable Bool
|
|
where assertionPredicate = return
|
|
|
|
instance (AssertionPredicable t) => AssertionPredicable (IO t)
|
|
where assertionPredicate = (>>= assertionPredicate)
|
|
</pre>
|
|
<p>The overloaded <tt>assert</tt> function in the <tt>Assertable</tt> type class constructs
|
|
an assertion.</p>
|
|
<pre>
|
|
class Assertable t
|
|
where assert :: t -> Assertion
|
|
|
|
instance Assertable ()
|
|
where assert = return
|
|
|
|
instance Assertable Bool
|
|
where assert = assertBool ""
|
|
|
|
instance (ListAssertable t) => Assertable [t]
|
|
where assert = listAssert
|
|
|
|
instance (Assertable t) => Assertable (IO t)
|
|
where assert = (>>= assert)
|
|
</pre>
|
|
<p>The <tt>ListAssertable</tt> class allows <tt>assert</tt> to be applied to <tt>[Char]</tt>
|
|
(that is, <tt>String</tt>).</p>
|
|
<pre>
|
|
class ListAssertable t
|
|
where listAssert :: [t] -> Assertion
|
|
|
|
instance ListAssertable Char
|
|
where listAssert = assertString
|
|
</pre>
|
|
<p>With the above declarations, <tt>(assert ())</tt>,
|
|
<tt>(assert True)</tt>, and <tt>(assert "")</tt> (as well as
|
|
<tt>IO</tt> forms of these values, such as <tt>(return ())</tt>) are all
|
|
assertions that never fail, while <tt>(assert False)</tt> and
|
|
<tt>(assert "some failure message")</tt> (and their
|
|
<tt>IO</tt> forms) are assertions that always fail. You may define additional
|
|
instances for the type classes <tt>Assertable</tt>, <tt>ListAssertable</tt>, and
|
|
<tt>AssertionPredicable</tt> if that should be useful in your application.</p>
|
|
|
|
<p>The overloaded <tt>test</tt> function in the <tt>Testable</tt> type class constructs a
|
|
test.</p>
|
|
<pre>
|
|
class Testable t
|
|
where test :: t -> Test
|
|
|
|
instance Testable Test
|
|
where test = id
|
|
|
|
instance (Assertable t) => Testable (IO t)
|
|
where test = TestCase . assert
|
|
|
|
instance (Testable t) => Testable [t]
|
|
where test = TestList . map test
|
|
</pre>
|
|
<p>The <tt>test</tt> function makes a test from either an <tt>Assertion</tt> (using
|
|
<tt>TestCase</tt>), a list of <tt>Testable</tt> items (using <tt>TestList</tt>), or
|
|
a <tt>Test</tt> (making no change).</p>
|
|
|
|
<p>The following operators can be used to construct tests.</p>
|
|
<pre>
|
|
infix 1 ~?, ~=?, ~?=
|
|
infixr 0 ~:
|
|
|
|
(~?) :: (AssertionPredicable t) => t -> String -> Test
|
|
pred ~? msg = TestCase (pred @? msg)
|
|
|
|
(~=?) :: (Eq a, Show a) => a -> a -> Test
|
|
expected ~=? actual = TestCase (expected @=? actual)
|
|
|
|
(~?=) :: (Eq a, Show a) => a -> a -> Test
|
|
actual ~?= expected = TestCase (actual @?= expected)
|
|
|
|
(~:) :: (Testable t) => String -> t -> Test
|
|
label ~: t = TestLabel label (test t)
|
|
</pre>
|
|
<p><tt>(~?)</tt>, <tt>(~=?)</tt>, and <tt>(~?=)</tt> each make an assertion, as for
|
|
<tt>(@?)</tt>, <tt>(@=?)</tt>, and <tt>(@?=)</tt>, respectively, and then a test case
|
|
from that assertion. <tt>(~:)</tt> attaches a label to something that is
|
|
<tt>Testable</tt>. You may define additional instances for the type class
|
|
<tt>Testable</tt> should that be useful.</p>
|
|
|
|
<h2 id="RunningTests">Running Tests</h2>
|
|
|
|
<p>HUnit is structured to support multiple test controllers. The first subsection below
|
|
describes the <a href="#TestExecution">test execution</a> characteristics common to all
|
|
test controllers. The second subsection describes the <a href="#Text-BasedController"
|
|
>text-based controller</a> that is included with HUnit.</p>
|
|
|
|
<h3 id="TestExecution">Test Execution</h3>
|
|
|
|
<p>All test controllers share a common test execution model. They differ only in how the
|
|
results of test execution are shown.</p>
|
|
|
|
<p>The execution of a test (a value of type <tt>Test</tt>) involves the serial execution (in
|
|
the <tt>IO</tt> monad) of its constituent test cases. The test cases are executed in a
|
|
depth-first, left-to-right order. During test execution, four counts of test cases are
|
|
maintained:</p>
|
|
<pre>
|
|
data Counts = Counts { cases, tried, errors, failures :: Int }
|
|
deriving (Eq, Show, Read)
|
|
</pre>
|
|
<ul>
|
|
<li><tt>cases</tt> is the number of test cases included in the test. This number is a
|
|
static property of a test and remains unchanged during test execution.</li>
|
|
<li><tt>tried</tt> is the number of test cases that have been executed so far during the
|
|
test execution.</li>
|
|
<li><tt>errors</tt> is the number of test cases whose execution ended with an unexpected
|
|
exception being raised. Errors indicate problems with test cases, as opposed to the
|
|
code under test.</li>
|
|
<li><tt>failures</tt> is the number of test cases whose execution asserted failure.
|
|
Failures indicate problems with the code under test.</li>
|
|
</ul>
|
|
<p>Why is there no count for test case successes? The technical reason is that the counts
|
|
are maintained such that the number of test case successes is always equal to
|
|
<tt>(tried - (errors + failures))</tt>. The
|
|
psychosocial reason is that, with test-centered development and the expectation that
|
|
test failures will be few and short-lived, attention should be focused on the failures
|
|
rather than the successes.</p>
|
|
|
|
<p>As test execution proceeds, three kinds of reporting event are communicated to the test
|
|
controller. (What the controller does in response to the reporting events depends on the
|
|
controller.)</p>
|
|
<ul>
|
|
<li><i>start</i> -- Just prior to initiation of a test case, the path of the test case
|
|
and the current counts (excluding the current test case) are reported.</li>
|
|
<li><i>error</i> -- When a test case terminates with an error, the error message is
|
|
reported, along with the test case path and current counts (including the current
|
|
test case).</li>
|
|
<li><i>failure</i> -- When a test case terminates with a failure, the failure message is
|
|
reported, along with the test case path and current counts (including the current
|
|
test case).</li>
|
|
</ul>
|
|
<p>Typically, a test controller shows <i>error</i> and <i>failure</i> reports immediately
|
|
but uses the <i>start</i> report merely to update an indication of overall test
|
|
execution progress.</p>
|
|
|
|
<h3 id="Text-BasedController">Text-Based Controller</h3>
|
|
|
|
<p>A text-based test controller is included with HUnit.</p>
|
|
<pre>
|
|
runTestText :: PutText st -> Test -> IO (Counts, st)
|
|
</pre>
|
|
<p><tt>runTestText</tt> is generalized on a <i>reporting scheme</i> given as its first
|
|
argument. During execution of the test given as its second argument, the controller
|
|
creates a string for each reporting event and processes it according to the reporting
|
|
scheme. When test execution is complete, the controller returns the final counts along
|
|
with the final state for the reporting scheme.</p>
|
|
|
|
<p>The strings for the three kinds of reporting event are as follows.</p>
|
|
<ul>
|
|
<li>A <i>start</i> report is the result of the function <tt>showCounts</tt> applied to
|
|
the counts current immediately prior to initiation of the test case being started.</li>
|
|
<li>An <i>error</i> report is of the form
|
|
"<tt>Error in: <i>path</i>\n<i>message</i></tt>",
|
|
where <i>path</i> is the path of the test case in error, as shown by
|
|
<tt>showPath</tt>, and <i>message</i> is a message describing the error. If the path
|
|
is empty, the report has the form "<tt>Error:\n<i>message</i></tt>".</li>
|
|
<li>A <i>failure</i> report is of the form
|
|
"<tt>Failure in: <i>path</i>\n<i>message</i></tt>", where
|
|
<i>path</i> is the path of the test case in error, as shown by
|
|
<tt>showPath</tt>, and <i>message</i> is the failure message. If the path is empty,
|
|
the report has the form "<tt>Failure:\n<i>message</i></tt>".</li>
|
|
</ul>
|
|
|
|
<p>The function <tt>showCounts</tt> shows a set of counts.</p>
|
|
<pre>
|
|
showCounts :: Counts -> String
|
|
</pre>
|
|
<p>The form of its result is
|
|
"<tt>Cases: <i>cases</i> Tried: <i>tried</i> Errors: <i>errors</i> Failures: <i>failures</i></tt>"
|
|
where <i>cases</i>, <i>tried</i>, <i>errors</i>, and <i>failures</i> are the count
|
|
values.</p>
|
|
|
|
<p>The function <tt>showPath</tt> shows a test case path.</p>
|
|
<pre>
|
|
showPath :: Path -> String
|
|
</pre>
|
|
<p>The nodes in the path are reversed (so that the path reads from the root down to the test
|
|
case), and the representations for the nodes are joined by '<tt>:</tt>' separators. The
|
|
representation for <tt>(ListItem <i>n</i>)</tt> is <tt>(show n)</tt>. The representation
|
|
for <tt>(Label <i>label</i>)</tt> is normally <i>label</i>. However, if <i>label</i>
|
|
contains a colon or if <tt>(show <i>label</i>)</tt> is different from <i>label</i>
|
|
surrounded by quotation marks--that is, if any ambiguity could exist--then <tt>(Label
|
|
<i>label</i>)</tt> is represented as <tt>(show <i>label</i>)</tt>.</p>
|
|
|
|
<p>HUnit includes two reporting schemes for the text-based test controller. You may define
|
|
others if you wish.</p>
|
|
<pre>
|
|
putTextToHandle :: Handle -> Bool -> PutText Int
|
|
</pre>
|
|
<p><tt>putTextToHandle</tt> writes error and failure reports, plus a report of the final
|
|
counts, to the given handle. Each of these reports is terminated by a newline. In
|
|
addition, if the given flag is <tt>True</tt>, it writes start reports to the handle as
|
|
well. A start report, however, is not terminated by a newline. Before the next report is
|
|
written, the start report is "erased" with an appropriate sequence of carriage return
|
|
and space characters. Such overwriting realizes its intended effect on terminal devices.</p>
|
|
<pre>
|
|
putTextToShowS :: PutText ShowS
|
|
</pre>
|
|
<p><tt>putTextToShowS</tt> ignores start reports and simply accumulates error and failure
|
|
reports, terminating them with newlines. The accumulated reports are returned (as the
|
|
second element of the pair returned by <tt>runTestText</tt>) as a <tt>ShowS</tt>
|
|
function (that is, one with type <tt>(String -> String)</tt>) whose
|
|
first argument is a string to be appended to the accumulated report lines.</p>
|
|
|
|
<p>HUnit provides a shorthand for the most common use of the text-based test controller.</p>
|
|
<pre>
|
|
runTestTT :: Test -> IO Counts
|
|
</pre>
|
|
<p><tt>runTestTT</tt> invokes <tt>runTestText</tt>, specifying <tt>(putTextToHandle stderr
|
|
True)</tt> for the reporting scheme, and returns the final counts from the test
|
|
execution.</p>
|
|
|
|
|
|
<h2 id="References">References</h2>
|
|
|
|
<dl>
|
|
|
|
<dt id="DesignPatterns">[1] Gamma, E., et al. Design Patterns: Elements of Reusable
|
|
Object-Oriented Software, Addison-Wesley, Reading, MA, 1995.</dt>
|
|
<dd>The classic book describing design patterns in an object-oriented context.</dd>
|
|
|
|
<dt>
|
|
<a href="http://www.junit.org">http://www.junit.org</a>
|
|
</dt>
|
|
<dd>Web page for JUnit, the tool after which HUnit is modeled.</dd>
|
|
|
|
<dt>
|
|
<a href="http://junit.sourceforge.net/doc/testinfected/testing.htm">
|
|
http://junit.sourceforge.net/doc/testinfected/testing.htm</a>
|
|
</dt>
|
|
<dd>A good introduction to test-first development and the use of JUnit.</dd>
|
|
|
|
<dt>
|
|
<a href="http://junit.sourceforge.net/doc/cookstour/cookstour.htm">
|
|
http://junit.sourceforge.net/doc/cookstour/cookstour.htm</a>
|
|
</dt>
|
|
<dd>A description of the internal structure of JUnit. Makes for an interesting
|
|
comparison between JUnit and HUnit.</dd>
|
|
|
|
</dl>
|
|
|
|
<hr/>
|
|
|
|
<p>The HUnit software and this guide were written by Dean Herington (<a
|
|
href="mailto:heringto@cs.unc.edu">heringto@cs.unc.edu</a>).</p>
|
|
</body>
|
|
</html>
|