-- | 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 []