packages feed

hspec 1.0.0.1 → 1.1.0

raw patch · 14 files changed

+573/−458 lines, 14 filesdep +hspec-shouldbe

Dependencies added: hspec-shouldbe

Files

Specs.hs view
@@ -1,109 +1,33 @@-{-# LANGUAGE StandaloneDeriving #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Specs where+{-# OPTIONS_GHC -fno-warn-deprecations #-}+module Main (main) where -import Test.Hspec-import Test.Hspec.Runner (hHspecWithFormat, toExitCode)-import Test.Hspec.Core (Spec(..), Result(..), quantify, failedCount, evaluateExample)-import Test.Hspec.Formatters-import Test.Hspec.QuickCheck-import Test.Hspec.HUnit ()-import Test.HUnit-import System.IO-import System.IO.Silently-import System.Environment-import System.Exit (exitWith)-import Data.List (isPrefixOf)-import qualified Test.HUnit as HUnit+import           Test.Hspec.ShouldBe (Specs, describe, it, hspecX) -deriving instance Show Result+import qualified Test.Hspec as H+import           Test.Hspec hiding (Specs, describe, it, hspecX)+import           Test.Hspec.Runner (hHspecWithFormat)+import           Test.Hspec.Internal (SpecTree(..), Spec(..), Result(..), quantify)+import           Test.Hspec.Formatters+import           Test.Hspec.QuickCheck+import           Test.Hspec.HUnit ()+import           Test.HUnit+import           System.IO+import           System.IO.Silently+import           Data.List (isPrefixOf)+import qualified Test.HUnit as HUnit  main :: IO ()-main = do-  ar <- getArgs-  specs' <- specs-  ss <- case ar of-    ["README"] -> withFile "README" WriteMode (\ h -> hPutStrLn h preamble >> hHspec h specs')-    [filename] -> withFile filename WriteMode (\ h -> hHspec h specs')-    _          -> hspec specs'-  exitWith $ toExitCode (failedCount ss == 0)--preamble :: String-preamble = unlines [-    "hspec aims to be a simple, extendable, and useful tool for Behavior Driven Development in Haskell.", "",-    "",-    "Step 1, write descriptions and examples of your desired behavior",-    "> module Myabs where",-    ">",-    "> import Test.Hspec",-    ">",-    "> specs :: Specs",-    "> specs = describe \"myabs\" [",-    ">   it \"returns the original number when given a positive input\"",-    ">     (myabs 1 == 1),",-    "> ",-    ">   it \"returns a positive number when given a negative input\"",-    ">     (myabs (-1) == 1),",-    "> ",-    ">   it \"returns zero when given zero\"",-    ">     (myabs 0 == 0)",-    ">   ]",-    "",-    "Step 2, write whatever you are describing",-    "> myabs n = undefined",-    "",-    "Step 3, watch your examples fail with red text by running from the .hs file itself",-    "> main = hspec specs",-    "",-    "myabs",-    " - returns the original number when given a positive input FAILED [1]",-    " - returns a positive number when given a negative input FAILED [2]",-    " - returns zero when given zero FAILED [3]",-    "",-    "1) myabs returns the original number when given a positive input FAILED",-    "Prelude.undefined",-    "",-    "2) myabs returns a positive number when given a negative input FAILED",-    "Prelude.undefined",-    "",-    "3) myabs returns zero when given zero FAILED",-    "Prelude.undefined",-    "",-    "Finished in 0.0002 seconds",-    "",-    "3 examples, 3 failures",-    "",-    "",-    "Specs can also be run from the command line using the hspec program",-    "  $ hspec myabs.hs",-    "",-    "Step 4, implement your desired behavior",-    "> myabs n = if n < 0 then negate n else n",-    "",-    "Step 5, watch your examples succeed with green text when rerun",-    "myabs",-    " - returns the original number when given a positive input",-    " - returns a positive number when given a negative input",-    " - returns zero when given zero",-    "",-    "Finished in 0.0000 seconds",-    "",-    "3 examples, 0 failures",-    "",-    "",-    "",-    "",-    "Here's the report of hspec's behavior:" ]+main = specs >>= hspecX  specs :: IO Specs specs = do-  let testSpecs = [describe "Example" [-          it "success" (Success),-          it "fail 1" (Fail "fail message"),-          it "pending" (Pending "pending message"),-          it "fail 2" (HUnit.assertEqual "assertEqual test" 1 (2::Int)),-          it "exceptions" (undefined :: Bool),-          it "quickcheck" (property $ \ i -> i == (i+1::Integer))]+  let testSpecs = [H.describe "Example" [+          H.it "success" (Success),+          H.it "fail 1" (Fail "fail message"),+          H.it "pending" (pending "pending message"),+          H.it "fail 2" (HUnit.assertEqual "assertEqual test" 1 (2::Int)),+          H.it "exceptions" (undefined :: Bool),+          H.it "quickcheck" (property $ \ i -> i == (i+1::Integer))]           ]    (reportContents, exampleSpecs)     <- capture $ hHspecWithFormat specdoc         False stdout testSpecs@@ -113,128 +37,122 @@    let report = lines reportContents -  return [-    describe "the \"describe\" function" [+  return $ do+    describe "the \"describe\" function" $ do         it "takes a description of what the behavior is for" $             case exampleSpecs of               [SpecGroup "Example" _] -> True               _ -> False-        ,+         it "groups behaviors for what's being described" $             case exampleSpecs of               [SpecGroup _ xs] -> length xs == 6               _ -> False-        ,-        describe "a nested description" [++        describe "a nested description" $ do             it "has it's own specs"                 (True)-        ]-    ],-    describe "the \"it\" function" [++    describe "the \"it\" function" $ do         it "takes a description of a desired behavior" $-            case it "whatever" Success of+            case unSpec $ H.it "whatever" Success of               SpecExample requirement _ -> requirement == "whatever"               _ -> False-        ,+         it "takes an example of that behavior" $ do-            case it "whatever" Success of+            case unSpec $ H.it "whatever" Success of               SpecExample _ example -> do-                r <- evaluateExample example+                r <- example                 r @?= Success               SpecGroup _ _ -> assertFailure "unexpected SpecGroup"-        ,+         it "can use a Bool, HUnit Test, QuickCheck property, or \"pending\" as an example"-            (True),+            (True)          it "will treat exceptions as failures"             (any (==" - exceptions FAILED [3]") report)-    ],-    describe "the \"hspec\" function" [++    describe "the \"hspec\" function" $ do         it "displays a header for each thing being described"-            (any (=="Example") report),+            (any (=="Example") report)          it "displays one row for each behavior"-            (HUnit.assertEqual "" 29 (length report)),+            (HUnit.assertEqual "" 29 (length report))          it "displays a row for each successfull, failed, or pending example"-            (any (==" - success") report && any (==" - fail 1 FAILED [1]") report),+            (any (==" - success") report && any (==" - fail 1 FAILED [1]") report)          it "displays a detailed list of failed examples"-            (any (=="1) Example fail 1 FAILED") report),+            (any (=="1) Example fail 1 FAILED") report)          it "displays a '#' with an additional message for pending examples"-            (any (=="     # pending message") report ),+            (any (=="     # PENDING: pending message") report )          it "summarizes the time it takes to finish"-            (any ("Finished in " `isPrefixOf`) report),+            (any ("Finished in " `isPrefixOf`) report)          it "summarizes the number of examples and failures"-            (any (=="6 examples, 4 failures") report),+            (any (=="6 examples, 4 failures") report)          it "outputs failed examples in red, pending in yellow, and successful in green"             (True)-    ],-    describe "Bool as an example" [++    describe "Bool as an example" $ do         it "is just an expression that evaluates to a Bool"             (True)-    ],-    describe "HUnit TestCase as an example" [++    describe "HUnit TestCase as an example" $ do         it "is specified with the HUnit \"TestCase\" data constructor"-            (HUnit.TestCase $ HUnit.assertBool "example" True),+            (HUnit.TestCase $ HUnit.assertBool "example" True)          it "is the assumed example for IO() actions"-            (HUnit.assertBool "example" True),+            (HUnit.assertBool "example" True) -        it "will show the failed assertion text if available (e.g. assertBool)"-            (HUnit.TestCase $ do-              (innerReport, _) <- capture $ hspec $ [describe "" [ it "" (HUnit.assertBool "trivial" False)]]-              HUnit.assertBool "should find assertion text" $ any (=="trivial") (lines innerReport)),+        it "will show the failed assertion text if available (e.g. assertBool)" $ do+          (innerReport, _) <- capture $ hspec $ [H.describe "" [ H.it "" (HUnit.assertBool "trivial" False)]]+          HUnit.assertBool "should find assertion text" $ any (=="trivial") (lines innerReport) -        it "will show the failed assertion expected and actual values if available (e.g. assertEqual)"-            (HUnit.TestCase $ do-              (innerReportContents, _) <- capture $ hspec $ [describe "" [ it "" (HUnit.assertEqual "trivial" (1::Int) 2)]]-              let innerReport = lines innerReportContents-              HUnit.assertBool "should find assertion text" $ any (=="trivial") innerReport-              HUnit.assertBool "should find 'expected: 1'" $ any (=="expected: 1") innerReport-              HUnit.assertBool "should find ' but got: 2'" $ any (==" but got: 2") innerReport)-    ],-    describe "QuickCheck property as an example" [+        it "will show the failed assertion expected and actual values if available (e.g. assertEqual)" $ do+          (innerReportContents, _) <- capture $ hspec $ [H.describe "" [ H.it "" (HUnit.assertEqual "trivial" (1::Int) 2)]]+          let innerReport = lines innerReportContents+          HUnit.assertBool "should find assertion text" $ any (=="trivial") innerReport+          HUnit.assertBool "should find 'expected: 1'" $ any (=="expected: 1") innerReport+          HUnit.assertBool "should find ' but got: 2'" $ any (==" but got: 2") innerReport++    describe "QuickCheck property as an example" $ do         it "is specified with the \"property\" function"-            (property $ \ b -> b || True),+            (property $ \ b -> b || True)          it "will show what falsified it"             (any (=="0") report)-    ],-    describe "pending as an example" [-        it "is specified with the \"pending\" function and an explanation"-            (pending "message" == Pending "message"), -        it "accepts a message to display in the report"-            (any (== "     # pending message") report)-    ],-    describe "the \"hHspecWithFormat\" function" [+    describe "pending as an example" $ do+        it "is specified with the \"pending\" function and an explanation" True++        it "accepts a message to display in the report" True++    describe "the \"hHspecWithFormat\" function" $ do         it "can use the \"silent\" formatter to show no output"-            (null silentReportContents),+            (null silentReportContents)          it "can use the \"progress\" formatter to show '..F...FF.F' style output"-            (HUnit.assertEqual "" ".F.FFF" (head $ lines progressReportContents)),+            (HUnit.assertEqual "" ".F.FFF" (head $ lines progressReportContents))          it "can use the \"specdoc\" formatter to show all examples (default)"-            (HUnit.assertEqual "" "Example" (lines reportContents !! 1)),+            (HUnit.assertEqual "" "Example" (lines reportContents !! 1))          it "can use the \"failed_examples\" formatter to show only failed examples"             (HUnit.assertEqual "" "1) Example fail 1 FAILED" (lines failed_examplesReportContents !! 1))-    ],-    describe "quantify (an internal function)" [++    describe "quantify (an internal function)" $ do         it "returns an amount and a word given an amount and word"-            (quantify (1::Int) "thing" == "1 thing"),+            (quantify (1::Int) "thing" == "1 thing")          it "returns a singular word given the number 1"-            (quantify (1::Int) "thing" == "1 thing"),+            (quantify (1::Int) "thing" == "1 thing")          it "returns a plural word given a number greater than 1"-            (quantify (2::Int) "thing" == "2 things"),+            (quantify (2::Int) "thing" == "2 things")          it "returns a plural word given the number 0"             (quantify (0::Int) "thing" == "0 things")-    ]]
Test/Hspec.hs view
@@ -1,28 +1,68 @@---- | Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is an approach--- to software development that combines Test-Driven Development, Domain Driven Design, and--- Acceptance Test-Driven Planning. Hspec helps you do the TDD part of that equation, focusing--- on the documentation and design aspects of TDD.+-- |+-- Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is+-- an approach to software development that combines Test-Driven Development,+-- Domain Driven Design, and Acceptance Test-Driven Planning. Hspec helps you+-- do the TDD part of that equation, focusing on the documentation and design+-- aspects of TDD. ----- Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of what applies to--- RSpec also applies to Hspec. Hspec ties together /descriptions/ of behavior and /examples/ of--- that behavior. The examples can also be run as tests and the output summarises what needs to--- be implemented.+-- Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of+-- what applies to RSpec also applies to Hspec. Hspec ties together+-- /descriptions/ of behavior and /examples/ of that behavior. The examples can+-- also be run as tests and the output summarises what needs to be implemented. ----- The three functions you'll use the most are 'hspec', 'describe', and 'it'. Here is an--- example of functions that format and unformat phone numbers and the specs for them.+-- NOTE: There is a monadic and a non-monadic API.  This is the documentation+-- for the non-monadic API.  The monadic API is more stable, so you may prefer+-- it over this one.  For documentation on the monadic API look at+-- "Test.Hspec.Monadic".++module Test.Hspec (++-- * Introduction+-- $intro++-- * Types+  Spec+, Specs+, Example+, Pending++-- * Defining a spec+, describe+, it+, pending++-- * Running a spec+, hspec+, hspecB+, hspecX+, hHspec++-- * Deprecated functions+, descriptions+) where++import           Test.Hspec.Core+import           Test.Hspec.Runner+import           Test.Hspec.Pending++-- $intro --+-- The three functions you'll use the most are 'hspecX', 'describe', and 'it'.+-- Here is an example of functions that format and unformat phone numbers and+-- the specs for them.+-- -- > import Test.Hspec -- > import Test.Hspec.QuickCheck--- > import Test.Hspec.HUnit+-- > import Test.Hspec.HUnit () -- > import Test.QuickCheck -- > import Test.HUnit -- >--- > main = hspec mySpecs+-- > main = hspecX mySpecs ----- Since the specs are often used to tell you what to implement, it's best to start with--- undefined functions. Once we have some specs, then you can implement each behavior--- one at a time, ensuring that each behavior is met and there is no undocumented behavior.+-- Since the specs are often used to tell you what to implement, it's best to+-- start with undefined functions. Once we have some specs, then you can+-- implement each behavior one at a time, ensuring that each behavior is met+-- and there is no undocumented behavior. -- -- > unformatPhoneNumber :: String -> String -- > unformatPhoneNumber number = undefined@@ -30,69 +70,50 @@ -- > formatPhoneNumber :: String -> String -- > formatPhoneNumber number = undefined ----- The 'describe' function takes a list of behaviors and examples bound together with the 'it' function+-- The 'describe' function takes a list of behaviors and examples bound+-- together with the 'it' function -- -- > mySpecs = [describe "unformatPhoneNumber" [ -- -- A boolean expression can act as a behavior's example. ----- >   it "removes dashes, spaces, and parenthesies"--- >       (unformatPhoneNumber "(555) 555-1234" == "5555551234"),+-- >   it "removes dashes, spaces, and parenthesies" $+-- >     unformatPhoneNumber "(555) 555-1234" == "5555551234"+-- >   , ----- The 'pending' function marks a behavior as pending an example. The example doesn't count as failing.+-- The 'pending' function marks a behavior as pending an example. The example+-- doesn't count as failing. ----- >   it "handles non-US phone numbers"--- >       (pending "need to look up how other cultures format phone numbers"),+-- >   it "handles non-US phone numbers" $+-- >     pending "need to look up how other cultures format phone numbers"+-- >   , ----- An HUnit 'Test' can act as a behavior's example. (must import @Test.Hspec.HUnit@)+-- An HUnit 'Test.HUnit.Test' can act as a behavior's example. (must import+-- "Test.Hspec.HUnit") ----- >   it "removes the \"ext\" prefix of the extension"--- >       (TestCase $ let expected = "5555551234135"--- >                       actual   = unformatPhoneNumber "(555) 555-1234 ext 135"--- >                   in assertEqual "remove extension" expected actual),+-- >   it "removes the \"ext\" prefix of the extension" $ TestCase $ do+-- >     let expected = "5555551234135"+-- >         actual   = unformatPhoneNumber "(555) 555-1234 ext 135"+-- >     expected @?= actual+-- >   , ----- An @IO()@ action is treated like an HUnit 'TestCase'. (must import @Test.Hspec.HUnit@)+-- An @IO()@ action is treated like an HUnit 'TestCase'. (must import+-- "Test.Hspec.HUnit") ----- >   it "converts letters to numbers"--- >       (do--- >         let expected = "6862377"--- >         let actual   = unformatPhoneNumber "NUMBERS"--- >         assertEqual "letters to numbers" expected actual),+-- >   it "converts letters to numbers" $ do+-- >     let expected = "6862377"+-- >         actual   = unformatPhoneNumber "NUMBERS"+-- >     actual @?= expected+-- >   , ----- The 'property' function allows a QuickCheck property to act as an example. (must import @Test.Hspec.QuickCheck@)+-- The 'property' function allows a QuickCheck property to act as an example.+-- (must import "Test.Hspec.QuickCheck") ----- >   it "can add and remove formatting without changing the number"--- >       (property $ forAll phoneNumber $--- >         \ n -> unformatPhoneNumber (formatPhoneNumber n) == n)+-- >   it "can add and remove formatting without changing the number" $ property $+-- >     forAll phoneNumber $ \n -> unformatPhoneNumber (formatPhoneNumber n) == n -- >   ]] -- > -- > phoneNumber :: Gen String -- > phoneNumber = do--- >   nums <- elements [7,10,11,12,13,14,15]--- >   vectorOf nums (elements "0123456789")----module Test.Hspec (--  -- * Types-    Spec-  , Result-  , Specs--  -- * Defining a spec-  , describe-  , it-  , pending--  -- * Running a spec-  , hspec-  , hspecB-  , hspecX-  , hHspec--  -- * Deprecated functions-  , descriptions-) where--import Test.Hspec.Core-import Test.Hspec.Runner-+-- >   n <- elements [7,10,11,12,13,14,15]+-- >   vectorOf n (elements "0123456789")
Test/Hspec/Core.hs view
@@ -1,106 +1,78 @@-{-# OPTIONS -XFlexibleInstances -XExistentialQuantification #-}---- | This module contains the core types, constructors, classes,--- instances, and utility functions common to hspec.----module Test.Hspec.Core where+{-# OPTIONS_GHC -fno-warn-deprecations #-}+-- |+-- This module contains the core types, constructors, classes, instances, and+-- utility functions common to hspec.+module Test.Hspec.Core (+  SpecTree (..)+, Example (..)+, Result (..)+, descriptions -import System.IO.Silently-import Control.Exception+, describe+, it+, Spec+, Specs+, UnevaluatedSpec+, EvaluatedSpec --- | The result of running an example.-data Result = Success | Pending String | Fail String-  deriving Eq+, quantify +-- * Deprecated types and functions+-- | The following types and functions are deprecated and will be removed with+-- the next release.+--+-- If you still need any of those, please open an issue and describe your use+-- case: <https://github.com/hspec/hspec/issues>+, AnyExample+, safeEvaluateExample -type UnevaluatedSpec = Spec AnyExample-type EvaluatedSpec = Spec Result+, success+, failure+, isFailure+, failedCount+) where -data Spec a = SpecGroup String [Spec a]-            | SpecExample String a+import           Test.Hspec.Internal hiding (safeEvaluateExample)+import qualified Test.Hspec.Internal as Internal -describe :: String -> [Spec a] -> Spec a-describe = SpecGroup+{-# DEPRECATED UnevaluatedSpec "use Spec instead" #-}+type UnevaluatedSpec = Spec  -- | DEPRECATED: This is no longer needed (it's just an alias for `id` now).-descriptions :: [Spec a] -> [Spec a]+descriptions :: Specs -> Specs descriptions = id {-# DEPRECATED descriptions "this is no longer needed, and will be removed in a future release" #-} -safeEvaluateExample :: AnyExample -> IO Result-safeEvaluateExample example' = do-  evaluateExample example' `catches` [-    -- Re-throw AsyncException, otherwise execution will not terminate on-    -- SIGINT (ctrl-c).  All AsyncExceptions are re-thrown (not just-    -- UserInterrupt) because all of them indicate severe conditions and-    -- should not occur during normal test runs.-    Handler (\e -> throw (e :: AsyncException)),--    Handler (\e -> return $ Fail (show (e :: SomeException)))-    ]----- | Create a set of specifications for a specific type being described.--- Once you know what you want specs for, use this.------ > describe "abs" [--- >   it "returns a positive number given a negative number"--- >     (abs (-1) == 1)--- >   ]----it :: Example a => String -> a -> UnevaluatedSpec-it requirement' example' = SpecExample requirement' (AnyExample example')--class Example a where-  evaluateExample :: a -> IO Result--instance Example Bool where-  evaluateExample bool = evaluateExample $ if bool then Success else Fail ""--instance Example Result where-  evaluateExample result' = silence $ result' `seq` return result'---- | An existentially quantified @Example@. This way they can be mixed within the same set of Specs-data AnyExample = forall a. Example a => AnyExample a--instance Example AnyExample where-  evaluateExample (AnyExample a) = evaluateExample a------ | Declare an example as not successful or failing but pending some other work.--- If you want to report on a behavior but don't have an example yet, use this.------ > describe "fancyFormatter" [--- >   it "can format text in a way that everyone likes"--- >     (pending "waiting for clarification from the designers")--- >   ]----pending :: String  -- ^ An explanation for why this behavior is pending.-        -> Result-pending = Pending+type Specs = [Spec] +type EvaluatedSpec = SpecTree Result -failedCount :: [EvaluatedSpec] -> Int-failedCount = sum . map count-  where-    count (SpecGroup _ xs) = sum (map count xs)-    count (SpecExample _ x) = if isFailure x then 1 else 0+{-# DEPRECATED success "This will be removed with the next major release.  If you still need this, raise your voice!" #-}+success :: [EvaluatedSpec] -> Bool+success = not . failure +{-# DEPRECATED failure "This will be removed with the next major release.  If you still need this, raise your voice!" #-} failure :: [EvaluatedSpec] -> Bool failure = any p   where     p (SpecGroup _ xs) = any p xs     p (SpecExample _ x) = isFailure x -success :: [EvaluatedSpec] -> Bool-success = not . failure-+{-# DEPRECATED isFailure "This will be removed with the next major release.  If you still need this, raise your voice!" #-} isFailure :: Result -> Bool isFailure (Fail _) = True isFailure _        = False --- | Create a more readable display of a quantity of something.-quantify :: (Show a, Num a, Eq a) => a -> String -> String-quantify 1 s = "1 " ++ s-quantify n s = show n ++ " " ++ s ++ "s"+{-# DEPRECATED failedCount "This will be removed with the next major release.  If you still need this, raise your voice!" #-}+failedCount :: [EvaluatedSpec] -> Int+failedCount = sum . map count+  where+    count (SpecGroup _ xs) = sum (map count xs)+    count (SpecExample _ x) = if isFailure x then 1 else 0++{-# DEPRECATED AnyExample "This will be removed with the next major release.  If you still need this, raise your voice!" #-}+type AnyExample  = IO Result++{-# DEPRECATED safeEvaluateExample "This will be removed with the next major release.  If you still need this, raise your voice!" #-}+safeEvaluateExample :: AnyExample -> IO Result+safeEvaluateExample = Internal.safeEvaluateExample
Test/Hspec/Formatters.hs view
@@ -38,10 +38,11 @@ , withFailColor ) where -import Test.Hspec.Core (quantify)-import Data.List (intersperse)-import Text.Printf-import Control.Monad (when)+import           Data.Maybe+import           Test.Hspec.Core (quantify)+import           Data.List (intersperse)+import           Text.Printf+import           Control.Monad (unless)  -- We use an explicit import list for "Test.Hspec.Formatters.Internal", to make -- sure, that we only use the public API to implement formatters.@@ -92,11 +93,11 @@     writeLine $ indentationForExample nesting ++ " - " ++ requirement  , exampleFailed = \nesting requirement _ -> withFailColor $ do-    failed <- getFailCount-    writeLine $ indentationForExample nesting ++ " - " ++ requirement ++ " FAILED [" ++ show failed ++ "]"+    n <- getFailCount+    writeLine $ indentationForExample nesting ++ " - " ++ requirement ++ " FAILED [" ++ show n ++ "]"  , examplePending = \nesting requirement reason -> withPendingColor $ do-    writeLine $ indentationForExample nesting ++ " - " ++ requirement ++ "\n     # " ++ reason+    writeLine $ indentationForExample nesting ++ " - " ++ requirement ++ "\n     # PENDING: " ++ fromMaybe "No reason given" reason  , failedFormatter = defaultFailedFormatter @@ -129,7 +130,7 @@ defaultFailedFormatter = withFailColor $ do   failures <- getFailMessages   mapM_ writeLine ("" : intersperse "" failures)-  when (not $ null failures) (writeLine "")+  unless (null failures) (writeLine "")  defaultFooter :: FormatM () defaultFooter = do
Test/Hspec/Formatters/Internal.hs view
@@ -30,11 +30,11 @@ ) where  import qualified System.IO as IO-import System.IO (Handle)-import Control.Monad (when)-import Control.Exception (bracket_)-import System.Console.ANSI-import Control.Monad.Trans.State hiding (gets, modify)+import           System.IO (Handle)+import           Control.Monad (when)+import           Control.Exception (bracket_)+import           System.Console.ANSI+import           Control.Monad.Trans.State hiding (gets, modify) import qualified Control.Monad.Trans.State as State import qualified Control.Monad.IO.Class as IOClass import qualified System.CPUTime as CPUTime@@ -126,7 +126,7 @@ -- | evaluated after each failed example , exampleFailed       :: Int -> String -> String -> FormatM () -- | evaluated after each pending example-, examplePending      :: Int -> String -> String -> FormatM ()+, examplePending      :: Int -> String -> Maybe String -> FormatM () -- | evaluated after a test run , failedFormatter     :: FormatM () -- | evaluated after `failuresFormatter`@@ -182,7 +182,7 @@ getCPUTime = do   t1 <- liftIO CPUTime.getCPUTime   t0 <- gets cpuStartTime-  return ((fromIntegral $ t1 - t0) / (10.0^(12::Integer)))+  return (fromIntegral (t1 - t0) / (10.0^(12::Integer)))  -- | Get the passed real time since the test run has been started. getRealTime :: FormatM Double
Test/Hspec/HUnit.hs view
@@ -1,29 +1,37 @@-{-# OPTIONS -XFlexibleInstances -fno-warn-orphans #-}+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}+{-# OPTIONS -fno-warn-orphans #-} --- | Importing this module allows you to use an @HUnit@ test case as an example--- for a behavior. You can use an explicit @TestCase@ data constructor or--- use an @IO()@ action. For an @IO()@ action, any exception means the example--- failed; otherwise, it's successfull. Any output from the example to stdout is--- ignored. If you need to write out for debugging, you can write to stderr or--- a file handle.+-- |+-- Importing this module allows you to use an @HUnit@ `HU.Test` as an example+-- for a behavior.  You can use an explicit `HU.TestCase` data constructor or+-- use an `HU.Assertion`.  For an @Assertion@, any exception means the example+-- failed; otherwise, it's successfull. ----- > describe "cutTheDeck" [--- >   it "puts the first half of a list after the last half"--- >      (TestCase $ assertEqual "cut the deck" [3,4,1,2] (cutTheDeck [1,2,3,4])),+-- NOTE: Any output from the example to @stdout@ is ignored.  If you need to+-- write out for debugging, you can write to @stderr@ or a file handle.+--+-- > import Test.Hspec.Monadic+-- > import Test.Hspec.HUnit ()+-- > import Test.HUnit -- >--- >   it "restores an even sized list when cut twice"--- >      (assertEqual "cut the deck twice" [3,4,1,2] (cutTheDeck (cutTheDeck [1,2,3,4]))),--- >   ]+-- > main :: IO ()+-- > main = hspecX $ do+-- >   describe "reverse" $ do+-- >     it "reverses a list" $ do+-- >       reverse [1, 2, 3] @?= [3, 2, 1] -- >+-- >     it "gives the original list, if applied twice" $ TestCase $+-- >       (reverse . reverse) [1, 2, 3] @?= [1, 2, 3]+-- module Test.Hspec.HUnit ( ) where -import System.IO.Silently-import Test.Hspec.Core+import           System.IO.Silently+import           Test.Hspec.Core import qualified Test.HUnit as HU-import Data.List (intersperse)+import           Data.List (intersperse) -instance Example (IO ()) where+instance Example HU.Assertion where   evaluateExample io = evaluateExample (HU.TestCase io)  instance Example HU.Test where
+ Test/Hspec/Internal.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE FlexibleInstances #-}+module Test.Hspec.Internal (+  SpecTree (..)+, Spec (..)+, Example (..)+, safeEvaluateExample+, Result (..)++, describe+, it++, quantify+)+where++import           Control.Exception+import qualified Test.Hspec.Pending as Pending++-- | The result of running an example.+data Result = Success | Pending (Maybe String) | Fail String+  deriving (Eq, Show)++newtype Spec = Spec {unSpec :: SpecTree (IO Result)}++-- IMPORTANT: Remove this warnings and remove -fno-warn-deprecations from the+-- following modules/files when making SpecGroup abstract.+--+--  * Spec.hs+--  * Test.Hspec.Runner+--  * Test.Hspec.Core+--+{-# WARNING SpecExample "This will be removed from the public interface with the next major release.  If you need this, raise your voice!" #-}+{-# WARNING SpecGroup "This will be removed from the public interface with the next major release.  If you need this, raise your voice!" #-}++-- | Internal representation of a spec.+--+-- This will be made abstract with the next release.  If you still need access+-- to any constructors, open an issue and describe your use case:+-- <https://github.com/hspec/hspec/issues>+data SpecTree a = SpecGroup String [SpecTree a]+            | SpecExample String a++describe :: String -> [Spec] -> Spec+describe str specs = Spec . SpecGroup str $ map unSpec specs++safeEvaluateExample :: IO Result -> IO Result+safeEvaluateExample action = do+  action `catches` [+    -- Re-throw AsyncException, otherwise execution will not terminate on+    -- SIGINT (ctrl-c).  All AsyncExceptions are re-thrown (not just+    -- UserInterrupt) because all of them indicate severe conditions and+    -- should not occur during normal test runs.+    Handler (\e -> throw (e :: AsyncException)),++    Handler (\e -> return $ Fail (show (e :: SomeException)))+    ]+++-- | Create a set of specifications for a specific type being described.+-- Once you know what you want specs for, use this.+--+-- > describe "abs" [+-- >   it "returns a positive number given a negative number"+-- >     (abs (-1) == 1)+-- >   ]+--+it :: Example a => String -> a -> Spec+it requirement' = Spec . SpecExample requirement' . evaluateExample++-- | A type class for examples.+--+-- To use an HUnit `Test.HUnit.Test` or an `Test.HUnit.Assertion` as an example+-- you need to import "Test.Hspec.HUnit".+--+-- To use a QuickCheck `Test.QuickCheck.Property` as an example you need to+-- import "Test.Hspec.QuickCheck".+class Example a where+  evaluateExample :: a -> IO Result++instance Example Bool where+  evaluateExample b = if b then return Success else return (Fail "")++instance Example Result where+  evaluateExample r = r `seq` return r++instance Example Pending.Pending where+  evaluateExample (Pending.Pending reason) = evaluateExample (Pending reason)++instance Example (String -> Pending.Pending) where+  evaluateExample _ = evaluateExample (Pending.Pending Nothing)++-- | Create a more readable display of a quantity of something.+quantify :: (Show a, Num a, Eq a) => a -> String -> String+quantify 1 s = "1 " ++ s+quantify n s = show n ++ " " ++ s ++ "s"
Test/Hspec/Monadic.hs view
@@ -1,21 +1,66 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-}--- | This module contains the runners that take a set of specs, specified in a monadic style, evaluate their examples, and--- report to a given handle.+-- |+-- There is a monadic and a non-monadic API.  This is the documentation for the+-- monadic API.  The monadic API is suitable for most use cases, and it is more+-- stable than the non-monadic API. ----- The three functions you'll use the most are 'hspec', 'describe', and 'it'. Here is an--- example of functions that format and unformat phone numbers and the specs for them.+-- For documentation on the non-monadic API look at "Test.Hspec". --+module Test.Hspec.Monadic (+-- * Introduction+-- $intro+-- * Types+  Specs+, Example+, Pending++-- * Defining a spec+, describe+-- , context+, it+, pending++-- * Running a spec+, hspec+, hspecB+, hspecX+, hHspec++-- * Interface to the non-monadic API+, runSpecM+, fromSpecList++-- * Deprecated functions+, descriptions+) where++import           System.IO+import           Test.Hspec.Core (EvaluatedSpec, Example)+import qualified Test.Hspec.Core as Core+import qualified Test.Hspec.Runner as Runner+import           Test.Hspec.Pending (Pending)+import qualified Test.Hspec.Pending as Pending++import           Control.Monad.Trans.Writer (Writer, execWriter, tell)++-- $intro+--+-- The three functions you'll use the most are 'hspecX', 'describe', and 'it'.+-- Here is an example of functions that format and unformat phone numbers and+-- the specs for them.+-- -- > import Test.Hspec.Monadic -- > import Test.Hspec.QuickCheck--- > import Test.Hspec.HUnit+-- > import Test.Hspec.HUnit () -- > import Test.QuickCheck -- > import Test.HUnit -- >--- > main = hspec mySpecs+-- > main = hspecX mySpecs ----- Since the specs are often used to tell you what to implement, it's best to start with--- undefined functions. Once we have some specs, then you can implement each behavior--- one at a time, ensuring that each behavior is met and there is no undocumented behavior.+-- Since the specs are often used to tell you what to implement, it's best to+-- start with undefined functions. Once we have some specs, then you can+-- implement each behavior one at a time, ensuring that each behavior is met+-- and there is no undocumented behavior. -- -- > unformatPhoneNumber :: String -> String -- > unformatPhoneNumber number = undefined@@ -23,7 +68,8 @@ -- > formatPhoneNumber :: String -> String -- > formatPhoneNumber number = undefined ----- The 'describe' function takes a list of behaviors and examples bound together with the 'it' function+-- The 'describe' function takes a list of behaviors and examples bound+-- together with the 'it' function -- -- > mySpecs = describe "unformatPhoneNumber" $ do --@@ -32,79 +78,54 @@ -- >   it "removes dashes, spaces, and parenthesies" $ -- >     unformatPhoneNumber "(555) 555-1234" == "5555551234" ----- The 'pending' function marks a behavior as pending an example. The example doesn't count as failing. --+-- The 'pending' function marks a behavior as pending an example. The example+-- doesn't count as failing.+-- -- >   it "handles non-US phone numbers" $ -- >     pending "need to look up how other cultures format phone numbers" ----- An HUnit 'Test' can act as a behavior's example. (must import @Test.Hspec.HUnit@) ----- >   it "removes the \"ext\" prefix of the extension" $ do+-- An HUnit 'Test.HUnit.Test' can act as a behavior's example. (must import+-- "Test.Hspec.HUnit")+--+-- >   it "removes the \"ext\" prefix of the extension" $ TestCase $ do -- >     let expected = "5555551234135" -- >         actual   = unformatPhoneNumber "(555) 555-1234 ext 135"--- >     assertEqual "remove extension" expected actual+-- >     expected @?= actual ----- An @IO()@ action is treated like an HUnit 'TestCase'. (must import @Test.Hspec.HUnit@) --+-- An @IO()@ action is treated like an HUnit 'TestCase'. (must import+-- "Test.Hspec.HUnit")+-- -- >   it "converts letters to numbers" $ do -- >     let expected = "6862377" -- >         actual   = unformatPhoneNumber "NUMBERS"--- >     assertEqual "letters to numbers" expected actual+-- >     actual @?= expected ----- The 'property' function allows a QuickCheck property to act as an example. (must import @Test.Hspec.HUnit@) --+-- The 'property' function allows a QuickCheck property to act as an example.+-- (must import "Test.Hspec.QuickCheck")+-- -- >   it "can add and remove formatting without changing the number" $ property $--- >     forAll phoneNumber $ \ n -> unformatPhoneNumber (formatPhoneNumber n) == n+-- >     forAll phoneNumber $ \n -> unformatPhoneNumber (formatPhoneNumber n) == n -- > -- > phoneNumber :: Gen String -- > phoneNumber = do--- >   nums <- elements [7,10,11,12,13,14,15]--- >   vectorOf nums (elements "0123456789")+-- >   n <- elements [7,10,11,12,13,14,15]+-- >   vectorOf n (elements "0123456789") -- -module Test.Hspec.Monadic (--  -- * Types-    Spec-  , Result-  , Specs--  -- * Defining a spec-  , describe-  , it-  , pending--  -- * Running a spec-  , hspec-  , hspecB-  , hspecX-  , hHspec--  -- * Interface to the non-monadic API-  , runSpecM-  , fromSpecList--  -- * Deprecated functions-  , descriptions-) where--import System.IO-import Test.Hspec.Core hiding (describe,descriptions,it)-import qualified Test.Hspec.Core as Core-import qualified Test.Hspec.Runner as Runner--import Control.Monad.Trans.Writer (Writer, execWriter, tell)- type Specs = SpecM () -newtype SpecM a = SpecM (Writer [UnevaluatedSpec] a)+newtype SpecM a = SpecM (Writer [Core.Spec] a)   deriving Monad  -- | Create a document of the given specs and write it to stdout. hspec :: Specs -> IO [EvaluatedSpec] hspec = Runner.hspec . runSpecM --- | Use in place of @hspec@ to also exit the program with an @ExitCode@+-- | Use in place of `hspec` to also exit the program with an @ExitCode@ hspecX :: Specs -> IO a hspecX = Runner.hspecX . runSpecM @@ -114,24 +135,45 @@  -- | Create a document of the given specs and write it to the given handle. ----- > writeReport filename specs = withFile filename WriteMode (\ h -> hHspec h specs)+-- > writeReport filename specs = withFile filename WriteMode (\h -> hHspec h specs) -- hHspec :: Handle -> Specs -> IO [EvaluatedSpec] hHspec h = Runner.hHspec h . runSpecM  -- | Convert a monadic spec into a non-monadic spec.-runSpecM :: Specs -> [UnevaluatedSpec]+runSpecM :: Specs -> [Core.Spec] runSpecM (SpecM specs) = execWriter specs  -- | Convert a non-monadic spec into a monadic spec.-fromSpecList :: [UnevaluatedSpec] -> Specs+fromSpecList :: [Core.Spec] -> Specs fromSpecList = SpecM . tell  describe :: String -> Specs -> Specs describe label action = SpecM . tell $ [Core.describe label (runSpecM action)] +{-+context :: String -> Specs -> Specs+context = describe+-}+ it :: Example v => String -> v -> Specs it label action = (SpecM . tell) [Core.it label action]++-- | A pending example.+--+-- If you want to report on a behavior but don't have an example yet, use this.+--+-- > describe "fancyFormatter" $ do+-- >   it "can format text in a way that everyone likes" $+-- >     pending+--+-- You can give an optional reason for why it's pending.+--+-- > describe "fancyFormatter" $ do+-- >   it "can format text in a way that everyone likes" $+-- >     pending "waiting for clarification from the designers"+pending :: String  -> Pending+pending = Pending.pending  -- | DEPRECATED: Use `sequence_` instead. descriptions :: [Specs] -> Specs
+ Test/Hspec/Pending.hs view
@@ -0,0 +1,21 @@+module Test.Hspec.Pending where++newtype Pending = Pending (Maybe String)++-- | A pending example.+--+-- If you want to report on a behavior but don't have an example yet, use this.+--+-- > describe "fancyFormatter" [+-- >   it "can format text in a way that everyone likes" $+-- >     pending+-- > ]+--+-- You can give an optional reason for why it's pending.+--+-- > describe "fancyFormatter" [+-- >   it "can format text in a way that everyone likes" $+-- >     pending "waiting for clarification from the designers"+-- > ]+pending :: String -> Pending+pending = Pending . Just
Test/Hspec/QuickCheck.hs view
@@ -1,33 +1,35 @@ {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}--- | Importing this module allows you to use a QuickCheck property as an example--- for a behavior. Use the 'property' function to indicate a QuickCkeck property.--- Any output from the example to stdout is ignored. If you need to write out for--- debugging, you can write to stderr or a file handle.+-- |+-- Importing this module allows you to use a QuickCheck `QC.Property` as an+-- example for a behavior.  Use `QC.property` to turn any `QC.Testable` into a+-- @Property@. ----- > describe "cutTheDeck" [--- >   it "puts the first half of a list after the last half"--- >      (property $ \ xs -> let top = take (length xs `div` 2) xs--- >                              bot = drop (length xs `div` 2) xs--- >                          in cutTheDeck xs == bot ++ top),+-- NOTE: Any output from the example to @stdout@ is ignored.  If you need to+-- write out for debugging, you can write to @stderr@ or a file handle.+--+-- > import Test.Hspec.Monadic+-- > import Test.Hspec.QuickCheck -- >--- >   it "restores an even sized list when cut twice"--- >      (property $ \ xs -> even (length xs) ==> cutTheDeck (cutTheDeck xs) == xs)--- >   ]+-- > main :: IO ()+-- > main = hspecX $ do+-- >   describe "reverse" $ do+-- >     it "gives the original list, if applied twice" $ property $+-- >       \xs -> (reverse . reverse) xs == (xs :: [Int]) -- module Test.Hspec.QuickCheck (   QC.property , prop ) where -import System.IO.Silently-import Test.Hspec.Core+import           System.IO.Silently+import           Test.Hspec.Core import qualified Test.QuickCheck as QC  -- just for the prop shortcut import qualified Test.Hspec.Monadic as DSL --- | Monadic DSL shortcut, use this instead of @it@+-- | Monadic DSL shortcut, use this instead of `DSL.it`. prop :: QC.Testable t => String -> t -> DSL.Specs prop n p = DSL.it n (QC.property p) 
Test/Hspec/Runner.hs view
@@ -1,4 +1,4 @@-+{-# OPTIONS_GHC -fno-warn-deprecations #-} -- | This module contains the runners that take a set of specs, evaluate their examples, and -- report to a given handle. --@@ -6,55 +6,69 @@   Specs, hspec, hspecX, hspecB, hHspec, hHspecWithFormat, describe, it, toExitCode ) where -import Test.Hspec.Core-import Test.Hspec.Formatters-import Test.Hspec.Formatters.Internal-import System.IO-import System.Exit--type Specs = [UnevaluatedSpec]+import           Test.Hspec.Internal+import           Test.Hspec.Core (EvaluatedSpec, Specs)+import           Test.Hspec.Formatters+import           Test.Hspec.Formatters.Internal+import           System.IO+import           System.Exit  -- | Evaluate and print the result of checking the spec examples.-runFormatter :: Formatter -> Int -> String -> UnevaluatedSpec -> FormatM EvaluatedSpec-runFormatter formatter nesting _ (SpecGroup group xs) = do-  exampleGroupStarted formatter (nesting) group-  ys <- mapM (runFormatter formatter (succ nesting) group) xs-  return (SpecGroup group ys)-runFormatter formatter nesting group (SpecExample requirement e) = do-  result <- liftIO $ safeEvaluateExample e-  case result of-    Success -> do-      increaseSuccessCount-      exampleSucceeded formatter nesting requirement-    Fail err -> do-      increaseFailCount-      exampleFailed  formatter nesting requirement err-      n <- getFailCount-      addFailMessage $ failureDetails group requirement err n-    Pending reason -> do-      increasePendingCount-      examplePending formatter nesting requirement reason-  return (SpecExample requirement result)+runFormatter :: Formatter -> Spec -> FormatM EvaluatedSpec+runFormatter formatter = go 0 "" . unSpec+  where+    go nesting _ (SpecGroup group xs) = do+      exampleGroupStarted formatter nesting group+      ys <- mapM (go (succ nesting) group) xs+      return (SpecGroup group ys)+    go nesting group (SpecExample requirement e) = do+      result <- liftIO $ safeEvaluateExample e+      case result of+        Success -> do+          increaseSuccessCount+          exampleSucceeded formatter nesting requirement+        Fail err -> do+          increaseFailCount+          exampleFailed  formatter nesting requirement err+          n <- getFailCount+          addFailMessage $ failureDetails group requirement err n+        Pending reason -> do+          increasePendingCount+          examplePending formatter nesting requirement reason+      return (SpecExample requirement result)  failureDetails :: String -> String -> String -> Int -> String failureDetails group requirement err i =   concat [ show i, ") ", group, " ",  requirement, " FAILED", if null err then "" else "\n" ++ err ] --- | Use in place of @hspec@ to also exit the program with an @ExitCode@+-- | Use in place of `hspec` to also exit the program with an @ExitCode@ hspecX :: Specs -> IO a hspecX ss = hspecB ss >>= exitWith . toExitCode  -- | Use in place of hspec to also give a @Bool@ success indication hspecB :: Specs -> IO Bool-hspecB ss = hspec ss >>= return . success+hspecB ss = success `fmap` hspec ss+  where+    success :: [EvaluatedSpec] -> Bool+    success = not . failure +    failure :: [EvaluatedSpec] -> Bool+    failure = any p+      where+        p (SpecGroup _ xs) = any p xs+        p (SpecExample _ x) = isFailure x++    isFailure :: Result -> Bool+    isFailure (Fail _) = True+    isFailure _        = False+ -- | Create a document of the given specs and write it to stdout. hspec :: Specs -> IO [EvaluatedSpec]-hspec ss = hHspec stdout ss+hspec = hHspec stdout  -- | Create a document of the given specs and write it to the given handle. ----- > writeReport filename specs = withFile filename WriteMode (\ h -> hHspec h specs)+-- > writeReport filename specs = withFile filename WriteMode (\h -> hHspec h specs) -- hHspec :: Handle -> Specs -> IO [EvaluatedSpec] hHspec h specs = do@@ -65,7 +79,7 @@ -- THIS IS LIKELY TO CHANGE hHspecWithFormat :: Formatter -> Bool -> Handle -> Specs -> IO [EvaluatedSpec] hHspecWithFormat formatter useColor h ss = runFormatM useColor h $ do-  specList <- mapM (runFormatter formatter 0 "") ss+  specList <- mapM (runFormatter formatter) ss   failedFormatter formatter   footerFormatter formatter   return specList@@ -73,4 +87,3 @@ toExitCode :: Bool -> ExitCode toExitCode True  = ExitSuccess toExitCode False = ExitFailure 1-
hspec.cabal view
@@ -1,5 +1,5 @@ name:           hspec-version:        1.0.0.1+version:        1.1.0 cabal-version:  >= 1.8 build-type:     Simple license:        BSD3@@ -19,7 +19,8 @@                 tests. Compared to other options, it provides a much nicer                 syntax that makes tests very easy to read.                 .-                Read the introductory documentation: <http://hspec.github.com/>+                New to Hspec?  Start with the introductory documentation:+                <http://hspec.github.com/>  source-repository head   type: git@@ -48,21 +49,37 @@     , Test.Hspec.QuickCheck    other-modules:+      Test.Hspec.Pending+      Test.Hspec.Internal       Test.Hspec.Formatters.Internal  test-suite spec   type:       exitcode-stdio-1.0-+  hs-source-dirs:+      ., test   main-is:-      runtests.hs--  other-modules:-      Specs-+      Spec.hs   ghc-options:       -Wall -Werror+  build-depends:+      base >= 4 && <= 5+    , silently >= 1.1.1 && < 2+    , ansi-terminal == 0.5.5+    , time < 1.5+    , transformers >= 0.2.0 && < 0.4.0+    , HUnit >= 1 && <= 2+    , QuickCheck >= 2.4.0.1 && <= 2.5+    , hspec-shouldbe +-- this should be merged with spec+test-suite old-spec+  type:+      exitcode-stdio-1.0+  main-is:+      Specs.hs+  ghc-options:+      -Wall -Werror   build-depends:       base >= 4 && <= 5     , silently >= 1.1.1 && < 2@@ -71,3 +88,4 @@     , transformers >= 0.2.0 && < 0.4.0     , HUnit >= 1 && <= 2     , QuickCheck >= 2.4.0.1 && <= 2.5+    , hspec-shouldbe
− runtests.hs
@@ -1,5 +0,0 @@-import Test.Hspec-import Specs (specs)--main :: IO ()-main = specs >>= hspecX
+ test/Spec.hs view
@@ -0,0 +1,9 @@+module Main (main) where++import           Test.Hspec.ShouldBe++import qualified Test.Hspec.MonadicSpec++main :: IO ()+main = hspecX $ do+  describe "Test.Hspec.Monadic" Test.Hspec.MonadicSpec.spec