在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):hspec/HUnit开源软件地址(OpenSource Url):https://github.com/hspec/HUnit开源编程语言(OpenSource Language):Haskell 100.0%开源软件介绍(OpenSource Introduction):HUnit User's GuideHUnit 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 IntroductionA test-centered methodology for software development is most effective when tests are easy to create, change, and execute. The JUnit tool pioneered support for test-first development in Java. HUnit is an adaptation of JUnit to Haskell, a general-purpose, purely functional programming language. (To learn more about Haskell, see 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 and running tests. The document concludes with a section describing HUnit's constituent files and a section giving references to further information. Getting StartedIn the Haskell module where your tests will reside, import module import Test.HUnit Define test cases as appropriate: 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) Name the test cases and group them together: tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2] Run the tests as a group. At a Haskell interpreter prompt, apply the
function > runTestTT tests
Cases: 2 Tried: 2 Errors: 0 Failures: 0
> If the tests are proving their worth, you might see: > runTestTT tests
### Failure in: 0:test1
for (foo 3),
expected: (1,2)
but got: (1,3)
Cases: 2 Tried: 2 Errors: 0 Failures: 1
> Isn't that easy? You can specify tests even more succinctly using operators and overloaded functions that HUnit provides: 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: > runTestTT tests
### Failure in: 0:test1:(foo 3)
expected: (1,2)
but got: (1,3)
Cases: 2 Tried: 2 Errors: 0 Failures: 1
> Writing TestsTests are specified compositionally. Assertions are combined to make a test case, and test cases are combined into tests. HUnit also provides advanced features for more convenient test specification. AssertionsThe basic building block of a test is an assertion. type Assertion = IO () An assertion is an assertFailure :: String -> Assertion
assertFailure msg = ioError (userError ("HUnit:" ++ msg))
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 Since assertions are Test CaseA 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
You can make a test case from an assertion by applying the TestsAs 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. data Test = TestCase Assertion
| TestList [Test]
| TestLabel String Test There are three important features of this definition to note:
The number of test cases that a test comprises can be computed with testCaseCount :: Test -> Int As mentioned above, a test is identified by its path in the test hierarchy. data Node = ListItem Int | Label String
deriving (Eq, Show, Read)
type Path = [Node] -- Node order is from test case to root. Each occurrence of 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 :: Test -> [Path] The three variants of The design of the type
Advanced FeaturesHUnit 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. 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 The 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 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 class ListAssertable t
where listAssert :: [t] -> Assertion
instance ListAssertable Char
where listAssert = assertString With the above declarations, The overloaded 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 The following operators can be used to construct tests. 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)
Running TestsHUnit is structured to support multiple test controllers. The first subsection below describes the test execution characteristics common to all test controllers. The second subsection describes the text-based controller that is included with HUnit. Test ExecutionAll 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 data Counts = Counts { cases, tried, errors, failures :: Int }
deriving (Eq, Show, Read)
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
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.)
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 ControllerA text-based test controller is included with HUnit. runTestText :: PutText st -> Test -> IO (Counts, st)
The strings for the three kinds of reporting event are as follows.
The function showCounts :: Counts -> String The form of its result is
The function 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 ' HUnit includes two reporting schemes for the text-based test controller. You may define others if you wish. putTextToHandle :: Handle -> Bool -> PutText Int
putTextToShowS :: PutText ShowS
HUnit provides a shorthand for the most common use of the text-based test controller. runTestTT :: Test -> IO Counts
References
The HUnit software and this guide were written by Dean Herington [email protected] |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论