packages feed

HUnit 1.3.1.0 → 1.3.1.1

raw patch · 7 files changed

+563/−567 lines, 7 filesnew-uploaderPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

+ CHANGELOG.md view
@@ -0,0 +1,19 @@+## Changes++#### 1.3.1.1++- Various updates to metadata and documentation removing outdated information and making other things more visible++### 1.3.1.0++- add minimal support for GHC 8.0++### 1.3.0.0++- removed support for old compilers++- add source locations for failing assertions (GHC >= 7.10.2 only)++#### 1.2.5.2++- Added support for GHC 7.7
HUnit.cabal view
@@ -1,23 +1,22 @@ Name:                   HUnit-Version:                1.3.1.0+Version:                1.3.1.1 Cabal-Version:          >= 1.8 License:                BSD3 License-File:           LICENSE Author:                 Dean Herington Maintainer:             Simon Hengel <sol@typeful.net> Stability:              stable-Homepage:               http://hunit.sourceforge.net/+Homepage:               https://github.com/hspec/HUnit#readme Category:               Testing Synopsis:               A unit testing framework for Haskell Description:     HUnit is a unit testing framework for Haskell, inspired by the     JUnit tool for Java, see: <http://www.junit.org>. Build-Type:             Simple-Data-Files:-    doc/Guide.html-    examples/Example.hs-    prologue.txt+Extra-Source-Files:+    CHANGELOG.md     README.md+    examples/Example.hs  source-repository head     type:     git@@ -38,7 +37,7 @@ Test-Suite tests     Type:               exitcode-stdio-1.0     Main-Is:            HUnitTests.hs-    HS-Source-Dirs:     tests+    HS-Source-Dirs:     tests, examples     Build-Depends:         base == 4.*,         deepseq,@@ -49,4 +48,5 @@         HUnitTestBase         HUnitTestExtended         TerminalTest+        Example     GHC-Options:        -Wall
README.md view
@@ -1,28 +1,545 @@-# HUnit+# HUnit User's Guide -HUnit is a unit testing framework for Haskell, inspired by the JUnit-tool for Java.  HUnit is free software; see its "License" file for-details.  HUnit is available at <http://hunit.sourceforge.net>.+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+[https://github.com/hspec/HUnit](https://github.com/hspec/HUnit) -HUnit 1.1.1 consists of a number of files.  Besides Haskell source files-in Test/HUnit (whose names end in ".hs" or ".lhs"), these files include:+## Introduction+A test-centered methodology for software development is most effective when tests are+easy to create, change, and execute. The [JUnit](www.junit.org) tool+pioneered support for test-first development in [Java](http://java.sun.com).+HUnit is an adaptation of JUnit to Haskell, a general-purpose, purely functional+programming language. (To learn more about Haskell, see www.haskell.org](http://www.haskell.org). +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?)++The next section helps you get started using HUnit in simple ways. Subsequent sections+give details on [writing tests](#writing-tests) and [running tests](#running-tests).+The document concludes with a section describing HUnit's [constituent files](#constituent-files)+and a section giving [references](#references) to further information.++## Getting Started++In the Haskell module where your tests will reside, import module `Test.HUnit`:++```haskell+import Test.HUnit ```-  * README.md       -- this file-  * doc/Guide.html  -- user's guide, in HTML format-  * LICENSE         -- license for use of HUnit++Define test cases as appropriate:++```haskell+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) ``` -See the user's guide for more information.+Name the test cases and group them together: -## Changes+```haskell+tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2]+``` -### 1.3.1.0+Run the tests as a group. At a Haskell interpreter prompt, apply the+function `runTestTT` to the collected tests. (The `TT` suggests+**T**ext orientation with output to the **T**erminal.) -- add minimal support for GHC 8.0+```haskell+> runTestTT tests+Cases: 2  Tried: 2  Errors: 0  Failures: 0+>+``` -### 1.3.0.0+If the tests are proving their worth, you might see: -- removed support for old compilers+```haskell+> runTestTT tests+### Failure in: 0:test1+for (foo 3),+expected: (1,2)+ but got: (1,3)+Cases: 2  Tried: 2  Errors: 0  Failures: 1+>+``` -- add source locations for failing assertions (GHC >= 7.10.2 only)+Isn't that easy?++You can specify tests even more succinctly using operators and+overloaded functions that HUnit provides:++```haskell+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" ]+```++Assuming the same test failures as before, you would see:++```haskell+> runTestTT tests+### Failure in: 0:test1:(foo 3)+expected: (1,2)+ but got: (1,3)+Cases: 2  Tried: 2  Errors: 0  Failures: 1+>+```++## Writing Tests++Tests are specified compositionally. [Assertions](#assertions) are+combined to make a [test case](#test-case), and test cases are combined+into [tests](#tests). HUnit also provides [advanced+features](#advanced-features) for more convenient test specification.++### Assertions++ The basic building block of a test is an **assertion**.++```haskell+type Assertion = IO ()+```++An assertion is an `IO` computation that always produces a void result. Why is an assertion an `IO` 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 `assertFailure`.++```haskell+assertFailure :: String -> Assertion+assertFailure msg = ioError (userError ("HUnit:" ++ msg))+```++`(assertFailure msg)` raises an exception. The string argument identifies the+ failure. The failure message is prefixed by "`HUnit:`" 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 `assertFailure` are subject to change and should+ not be relied upon.)++`assertFailure` can be used directly, but it is much more common to use it+ indirectly through other assertion functions that conditionally assert failure.++```haskell+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+```++With `assertBool` you give the assertion condition and failure message separately.+ With `assertString` the two are combined. With `assertEqual` 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 [Avanced Features](#advanced-features)++Since assertions are `IO` computations, they may be combined--along with other+     `IO` computations--using `(>>=)`, `(>>)`, and the `do`+ notation. As long as its result is of type `(IO ())`, 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.++### Test Case++A **test case** 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.++A test case consists of a single, possibly collective, assertion. The possibly multiple+ constituent assertions in a test case's collective assertion are **not** 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+     `IO.bracket` function to achieve the desired effect.++You can make a test case from an assertion by applying the `TestCase` constructor.+ For example, `(TestCase (return ()))` is a test case that never+ fails, and `(TestCase (assertEqual "for x," 3 x))`+ is a test case that checks that the value of `x` is 3.  Additional ways+ to create test cases are described later under [Advanced Features](#advanced-eatures).++### Tests++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.++In tune with the "composite" design pattern [1], a+ **test** 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.++```haskell+data Test = TestCase Assertion+          | TestList [Test]+          | TestLabel String Test+```++There are three important features of this definition to note:+++* A `TestList` consists of a list of tests rather than a list of test cases.+   This means that the structure of a `Test` is actually a tree. Using a+   hierarchy helps organize tests just as it helps organize files in a file system.+* A `TestLabel` 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.+* A `TestLabel` is separate from both `TestCase` and `TestList`.+   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.+++The number of test cases that a test comprises can be computed with `testCaseCount`.++```haskell+testCaseCount :: Test -> Int+```++As mentioned above, a test is identified by its **path** in the test hierarchy.++```haskell+data Node  = ListItem Int | Label String+  deriving (Eq, Show, Read)++type Path = [Node]    -- Node order is from test case to root.+```++Each occurrence of `TestList` gives rise to a `ListItem` and each+ occurrence of `TestLabel` gives rise to a `Label`. The `ListItem`s+ by themselves ensure uniqueness among test case paths, while the `Label`s allow+ you to add mnemonic names for individual test cases and collections of them.++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.++The paths of the test cases that a test comprises can be computed with+ `testCasePaths`. The paths are listed in the order in which the corresponding+ test cases would be executed.++```haskell+testCasePaths :: Test -> [Path]+```++The three variants of `Test` can be constructed simply by applying+ `TestCase`, `TestList`, and `TestLabel` to appropriate arguments.+ Additional ways to create tests are described later under [Advanced Features](#advanced-features).++The design of the type `Test` provides great conciseness, flexibility, and+ convenience in specifying tests. Moreover, the nature of Haskell significantly augments+ these qualities:++* Combining assertions and other code to construct test cases is easy with the+    `IO` monad.+* Using overloaded functions and special operators (see below), specification of+    assertions and tests is extremely compact.+* 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.+* Haskell's powerful abstraction facilities provide unmatched support for test+    refactoring.++### Advanced Features++HUnit provides additional features for specifying assertions and tests more conveniently+ and concisely. These facilities make use of Haskell type classes.++The following operators can be used to construct assertions.++```haskell+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+```++You provide a boolean condition and failure message separately to `(@?)`, as for+     `assertBool`, but in a different order. The `(@=?)` and `(@?=)`+ operators provide shorthands for `assertEqual` 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.)++The `(@?)` operator's first argument is something from which an assertion+ predicate can be made, that is, its type must be `AssertionPredicable`.++```haskell+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)+```++The overloaded `assert` function in the `Assertable` type class constructs+ an assertion.++```haskell+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)+```++The `ListAssertable` class allows `assert` to be applied to `[Char]`+ (that is, `String`).++```haskell+class ListAssertable t+ where listAssert :: [t] -> Assertion++instance ListAssertable Char+ where listAssert = assertString+```++With the above declarations, `(assert ())`,+ `(assert True)`, and `(assert "")` (as well as+ `IO` forms of these values, such as `(return ())`) are all+ assertions that never fail, while `(assert False)` and+     `(assert "some failure message")` (and their+     `IO` forms) are assertions that always fail. You may define additional+ instances for the type classes `Assertable`, `ListAssertable`, and+     `AssertionPredicable` if that should be useful in your application.++The overloaded `test` function in the `Testable` type class constructs a+ test.++```haskell+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+```++The `test` function makes a test from either an `Assertion` (using+     `TestCase`), a list of `Testable` items (using `TestList`), or+ a `Test` (making no change).++The following operators can be used to construct tests.++```haskell+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)+```++`(~?)`, `(~=?)`, and `(~?=)` each make an assertion, as for+ `(@?)`, `(@=?)`, and `(@?=)`, respectively, and then a test case+ from that assertion. `(~:)` attaches a label to something that is+ `Testable`. You may define additional instances for the type class+ `Testable` should that be useful.++## Running Tests++HUnit is structured to support multiple test controllers. The first+ subsection below describes the [test execution](#test-execution)+ characteristics common to all test controllers. The second subsection+ describes the text-based controller that is included with HUnit.++## Test Execution++All test controllers share a common test execution model. They differ only in how the+ results of test execution are shown.++The execution of a test (a value of type `Test`) involves the serial execution (in+ the `IO` 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:++```haskell+data Counts = Counts { cases, tried, errors, failures :: Int }+  deriving (Eq, Show, Read)+```+++* `cases` 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.+* `tried` is the number of test cases that have been executed so far during the+    test execution.+* `errors` 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.+* `failures` is the number of test cases whose execution asserted failure.+    Failures indicate problems with the code under test.+++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+     `(tried - (errors + failures))`. 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.++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.)++* *start* -- 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.+* *error* -- 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).+* *failure* -- 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).++Typically, a test controller shows *error* and *failure* reports immediately+ but uses the *start* report merely to update an indication of overall test+ execution progress.++### Text-Based Controller++A text-based test controller is included with HUnit.++```haskell+runTestText :: PutText st -> Test -> IO (Counts, st)+```++`runTestText` is generalized on a *reporting scheme* 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.++The strings for the three kinds of reporting event are as follows.++* A *start* report is the result of the function `showCounts` applied to+    the counts current immediately prior to initiation of the test case being started.+* An *error* report is of the form+            "`Error in:   *path*\n*message*`",+    where *path* is the path of the test case in error, as shown by+    `showPath`, and *message* is a message describing the error. If the path+    is empty, the report has the form "`Error:\n*message*`".+* A *failure* report is of the form+            "`Failure in: *path*\n*message*`", where+        *path* is the path of the test case in error, as shown by+    `showPath`, and *message* is the failure message. If the path is empty,+    the report has the form "`Failure:\n*message*`".++The function `showCounts` shows a set of counts.++```haskell+showCounts :: Counts -> String+```++The form of its result is+`Cases: *cases*  Tried: *tried*  Errors: *errors*  Failures: *failures*`+where *cases*, *tried*, *errors*, and *failures* are the count values.++The function `showPath` shows a test case path.++```haskell+ showPath :: Path -> String+```++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 '`:`' separators. The+ representation for `(ListItem *n*)` is `(show n)`. The representation+ for `(Label *label*)` is normally *label*. However, if *label*+ contains a colon or if `(show *label*)` is different from *label*+ surrounded by quotation marks--that is, if any ambiguity could exist--then `(Label+         *label*)` is represented as `(show *label*)`.++HUnit includes two reporting schemes for the text-based test controller. You may define+ others if you wish.++```haskell+putTextToHandle :: Handle -> Bool -> PutText Int+```++`putTextToHandle` 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 `True`, 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.++```haskell+putTextToShowS :: PutText ShowS+```++`putTextToShowS` 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 `runTestText`) as a `ShowS`+ function (that is, one with type `(String -> String)`) whose+ first argument is a string to be appended to the accumulated report lines.++HUnit provides a shorthand for the most common use of the text-based test controller.++```haskell+runTestTT :: Test -> IO Counts+```++`runTestTT` invokes `runTestText`, specifying `(putTextToHandle stderr+True)` for the reporting scheme, and returns the final counts from the+test execution.++## References++* [1] Gamma, E., et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1995: The classic book describing design patterns in an object-oriented context.++* [junit.org](http://www.junit.org): Web page for JUnit, the tool after which HUnit is modeled.++* [http://junit.sourceforge.net/doc/testinfected/testing.htm](http://junit.sourceforge.net/doc/testinfected/testing.htm): A good introduction to test-first development and the use of JUnit.++* [http://junit.sourceforge.net/doc/cookstour/cookstour.htm](http://junit.sourceforge.net/doc/cookstour/cookstour.htm): A description of the internal structure of JUnit. Makes for an interesting comparison between JUnit and HUnit.++The HUnit software and this guide were written by Dean Herington [heringto@cs.unc.edu](mailto:heringto@cs.unc.edu)
− doc/Guide.html
@@ -1,539 +0,0 @@-<!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) &lt;- partA 3-                         assertEqual "for the first result of partA," 5 x-                         b &lt;- 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) &lt;- 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&nbsp;(return&nbsp;()))</tt> is a test case that never-            fails, and-                <tt>(TestCase&nbsp;(assertEqual&nbsp;"for&nbsp;x,"&nbsp;3&nbsp;x))</tt>-            is a test case that checks that the value of <tt>x</tt> is 3.&nbsp; 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&nbsp;())</tt>,-            <tt>(assert&nbsp;True)</tt>, and <tt>(assert&nbsp;"")</tt> (as well as-            <tt>IO</tt> forms of these values, such as <tt>(return&nbsp;())</tt>) are all-            assertions that never fail, while <tt>(assert&nbsp;False)</tt> and-                <tt>(assert&nbsp;"some&nbsp;failure&nbsp;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&nbsp;-&nbsp;(errors&nbsp;+&nbsp;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&nbsp;in:&nbsp;&nbsp;&nbsp;<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&nbsp;in:&nbsp;<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:&nbsp;<i>cases</i>&nbsp;&nbsp;Tried:&nbsp;<i>tried</i>&nbsp;&nbsp;Errors:&nbsp;<i>errors</i>&nbsp;&nbsp;Failures:&nbsp;<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&nbsp;->&nbsp;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>
examples/Example.hs view
@@ -1,9 +1,9 @@ -- Example.hs  --  Examples from HUnit user's guide -- -- For more examples, check out the tests directory.  It contains unit tests--- for HUnit. +-- for HUnit. -module Main where+module Example where  import Test.HUnit @@ -36,5 +36,5 @@                               partB y @? "(partB " ++ show y ++ ") failed" ]  main :: IO Counts-main = do runTestTT tests+main = do _ <- runTestTT tests           runTestTT tests'
− prologue.txt
@@ -1,2 +0,0 @@-HUnit is a unit testing framework for Haskell, inspired by the JUnit-tool for Java, see: <http://www.junit.org>.
tests/HUnitTests.hs view
@@ -10,6 +10,7 @@ import HUnitTestBase import HUnitTestExtended import TerminalTest+import Example ()  main :: IO () main = do