tasty-hunit-0.10.1: Test/Tasty/HUnit.hs
-- | Unit testing support for tasty, inspired by the HUnit package.
--
-- Here's an example (a single tasty test case consisting of three
-- assertions):
--
-- >import Test.Tasty
-- >import Test.Tasty.HUnit
-- >
-- >main = defaultMain $
-- > testCase "Example test case" $ do
-- > -- assertion no. 1 (passes)
-- > 2 + 2 @?= 4
-- > -- assertion no. 2 (fails)
-- > assertBool "the list is not empty" $ null [1]
-- > -- assertion no. 3 (would have failed, but won't be executed because
-- > -- the previous assertion has already failed)
-- > "foo" @?= "bar"
{-# LANGUAGE TypeFamilies, DeriveDataTypeable #-}
module Test.Tasty.HUnit
(
-- * Constructing test cases
testCase
, testCaseInfo
, testCaseSteps
-- * Constructing assertions
, assertFailure
, assertBool
, assertEqual
, (@=?)
, (@?=)
, (@?)
, AssertionPredicable(..)
-- * Data types
, Assertion
, HUnitFailure(..)
-- * Accurate location for domain-specific assertion functions
-- | It is common to define domain-specific assertion functions based
-- on the standard ones, e.g.
--
-- > assertNonEmpty = assertBool "List is empty" . not . null
--
-- The problem is that if a test fails, tasty-hunit will point to the
-- definition site of @assertNonEmpty@ as the source of failure, not
-- its use site.
--
-- To correct this, add a 'HasCallStack' constraint (re-exported from
-- this module) to your function:
--
-- > assertNonEmpty :: HasCallStack => [a] -> Assertion
-- > assertNonEmpty = assertBool "List is empty" . not . null
--
, HasCallStack
-- * Deprecated functions and types
-- | These definitions come from HUnit, but I don't see why one would
-- need them. If you have a valid use case for them, please contact me
-- or file an issue for tasty. Otherwise, they will eventually be
-- removed.
, assertString
, Assertable(..)
, AssertionPredicate
) where
import Test.Tasty.Providers
import Test.Tasty.HUnit.Orig
import Test.Tasty.HUnit.Steps
import Data.Typeable
import Data.CallStack (HasCallStack)
import Control.Exception
-- | Turn an 'Assertion' into a tasty test case
testCase :: TestName -> Assertion -> TestTree
testCase name = singleTest name . TestCase . (fmap (const ""))
-- | Like 'testCase', except in case the test succeeds, the returned string
-- will be shown as the description. If the empty string is returned, it
-- will be ignored.
testCaseInfo :: TestName -> IO String -> TestTree
testCaseInfo name = singleTest name . TestCase
-- IO String is a computation that throws an exception upon failure or
-- returns an informational string otherwise. This allows us to unify the
-- implementation of 'testCase' and 'testCaseInfo'.
--
-- In case of testCase, we simply make the result string empty, which makes
-- tasty ignore it.
newtype TestCase = TestCase (IO String)
deriving Typeable
instance IsTest TestCase where
run _ (TestCase assertion) _ = do
-- The standard HUnit's performTestCase catches (almost) all exceptions.
--
-- This is bad for a few reasons:
-- - it interferes with timeout handling
-- - it makes exception reporting inconsistent across providers
-- - it doesn't provide enough information for ingredients such as
-- tasty-rerun
--
-- So we do it ourselves.
hunitResult <- try assertion
return $
case hunitResult of
Right info -> testPassed info
Left (HUnitFailure mbloc message) -> testFailed $ prependLocation mbloc message
testOptions = return []