{-# LANGUAGE BlockArguments #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wno-type-defaults #-} {-# OPTIONS_GHC -Wno-unused-do-bind #-} {-# LANGUAGE TypeOperators #-} module Test.MockCat.MockSpec (spec) where import qualified Control.Exception as E import Data.Function ((&)) import Test.Hspec import Test.MockCat.Mock ( any, expectByExpr, stubFn, shouldApplyInOrder, shouldApplyInPartialOrder, shouldApplyTimes, shouldApplyTimesGreaterThan, shouldApplyTimesGreaterThanEqual, shouldApplyTimesLessThan, shouldApplyTimesLessThanEqual, shouldApplyTo, createMock, createStubFn, createNamedMock, to, (|>) ) import Prelude hiding (any) spec :: Spec spec = do describe "Test of Mock" do describe "combination test" do it "arity = 1" do f <- createStubFn $ True |> False f True `shouldBe` False it "arity = 2" do f <- createStubFn $ True |> False |> True f True False `shouldBe` True it "arity = 3" do f <- createStubFn $ True |> "False" |> True |> "False" f True "False" True `shouldBe` "False" it "arity = 4" do f <- createStubFn $ True |> "False" |> True |> "False" |> True f True "False" True "False" `shouldBe` True it "Param |> a" do f <- createStubFn $ any |> False f True `shouldBe` False it "Param |> (a |> b)" do f <- createStubFn $ any |> False |> True f True False `shouldBe` True it "a |> (Param |> b)" do f <- createStubFn $ True |> any |> True f True False `shouldBe` True it "Param |> (Param |> a)" do f <- createStubFn $ any |> any |> True f True False `shouldBe` True it "a |> (Param |> (Param |> a))" do f <- createStubFn $ "any" |> any |> any |> True f "any" "any" "any" `shouldBe` True it "param |> (Param |> (Param |> a))" do f <- createStubFn $ any |> any |> any |> True f "any" "any" "any" `shouldBe` True mockTest Fixture { name = "arity = 1", create = createMock $ "a" |> False, apply = (`stubFn` "a"), applyFailed = Just (`stubFn` "x"), expected = False, verifyApply = (`shouldApplyTo` "a"), verifyApplyFailed = (`shouldApplyTo` "2"), verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` "a" } mockTest Fixture { name = "arity = 2", create = createMock $ "a" |> "b" |> True, apply = \m -> stubFn m "a" "b", applyFailed = Just (\m -> stubFn m "a" "x"), expected = True, verifyApply = \m -> shouldApplyTo m $ "a" |> "b", verifyApplyFailed = \m -> shouldApplyTo m $ "2" |> "b", verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` ("a" |> "b") } mockTest Fixture { name = "arity = 3", create = createMock $ "a" |> "b" |> "c" |> False, apply = \m -> stubFn m "a" "b" "c", applyFailed = Just (\m -> stubFn m "a" "b" "x"), expected = False, verifyApply = \m -> shouldApplyTo m $ "a" |> "b" |> "c", verifyApplyFailed = \m -> shouldApplyTo m $ "a" |> "b" |> "d", verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` ("a" |> "b" |> "c") } mockTest Fixture { name = "arity = 4", create = createMock $ "a" |> "b" |> "c" |> "d" |> True, apply = \m -> stubFn m "a" "b" "c" "d", applyFailed = Just (\m -> stubFn m "a" "b" "c" "x"), expected = True, verifyApply = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d", verifyApplyFailed = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "x", verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` ("a" |> "b" |> "c" |> "d") } mockTest Fixture { name = "arity = 5", create = createMock $ "a" |> "b" |> "c" |> "d" |> "e" |> False, apply = \m -> stubFn m "a" "b" "c" "d" "e", applyFailed = Just (\m -> stubFn m "a" "b" "c" "d" "x"), expected = False, verifyApply = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e", verifyApplyFailed = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "x", verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` ("a" |> "b" |> "c" |> "d" |> "e") } mockTest Fixture { name = "arity = 6", create = createMock $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> True, apply = \m -> stubFn m "a" "b" "c" "d" "e" "f", applyFailed = Just (\m -> stubFn m "a" "b" "c" "d" "e" "x"), expected = True, verifyApply = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e" |> "f", verifyApplyFailed = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e" |> "x", verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` ("a" |> "b" |> "c" |> "d" |> "e" |> "f") } mockTest Fixture { name = "arity = 7", create = createMock $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> False, apply = \m -> stubFn m "a" "b" "c" "d" "e" "f" "g", applyFailed = Just (\m -> stubFn m "a" "b" "c" "d" "e" "f" "x"), expected = False, verifyApply = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g", verifyApplyFailed = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "y", verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` ("a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g") } mockTest Fixture { name = "arity = 8", create = createMock $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> "h" |> False, apply = \m -> stubFn m "a" "b" "c" "d" "e" "f" "g" "h", applyFailed = Just (\m -> stubFn m "a" "b" "c" "d" "e" "f" "g" "x"), expected = False, verifyApply = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> "h", verifyApplyFailed = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> "x", verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` ("a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> "h") } mockTest Fixture { name = "arity = 9", create = createMock $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> "h" |> "i" |> False, apply = \m -> stubFn m "a" "b" "c" "d" "e" "f" "g" "h" "i", applyFailed = Just (\m -> stubFn m "a" "b" "c" "d" "e" "f" "g" "h" "x"), expected = False, verifyApply = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> "h" |> "i", verifyApplyFailed = \m -> shouldApplyTo m $ "a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> "h" |> "x", verifyApplyCount = \m c -> m `shouldApplyTimes` c `to` ("a" |> "b" |> "c" |> "d" |> "e" |> "f" |> "g" |> "h" |> "i") } describe "Test of Multi Mock" do mockTest Fixture { name = "arity = 1", create = createMock [ "1" |> True, "2" |> False ], expected = [ True, False ], apply = \m -> [stubFn m "1", stubFn m "2"], applyFailed = Just \m -> [stubFn m "3"], verifyApply = \m -> do m `shouldApplyTo` "1" m `shouldApplyTo` "2", verifyApplyFailed = (`shouldApplyTo` "3"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` "1" m `shouldApplyTimes` c `to` "2" } mockTest Fixture { name = "arity = 2", create = createMock [ "1" |> "2" |> True, "2" |> "3" |> False ], expected = [ True, False ], apply = \m -> [ stubFn m "1" "2", stubFn m "2" "3" ], applyFailed = Just \m -> [stubFn m "1" "x"], verifyApply = \m -> do m `shouldApplyTo` ("1" |> "2") m `shouldApplyTo` ("2" |> "3"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` ("1" |> "2") m `shouldApplyTimes` c `to` ("2" |> "3"), verifyApplyFailed = \m -> m `shouldApplyTo` ("1" |> "x") } mockTest Fixture { name = "arity = 3", create = createMock [ "1" |> "2" |> "3" |> True, "2" |> "3" |> "4" |> False ], expected = [ True, False ], apply = \m -> [ stubFn m "1" "2" "3", stubFn m "2" "3" "4" ], applyFailed = Just \m -> [stubFn m "1" "2" "x"], verifyApply = \m -> do m `shouldApplyTo` ("1" |> "2" |> "3") m `shouldApplyTo` ("2" |> "3" |> "4"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` ("1" |> "2" |> "3") m `shouldApplyTimes` c `to` ("2" |> "3" |> "4"), verifyApplyFailed = \m -> m `shouldApplyTo` ("1" |> "2" |> "x") } mockTest Fixture { name = "arity = 4", create = createMock [ "1" |> "2" |> "3" |> "4" |> True, "2" |> "3" |> "4" |> "5" |> False ], expected = [ True, False ], apply = \m -> [ stubFn m "1" "2" "3" "4", stubFn m "2" "3" "4" "5" ], applyFailed = Just \m -> [stubFn m "1" "2" "3" "x"], verifyApply = \m -> do m `shouldApplyTo` ("1" |> "2" |> "3" |> "4") m `shouldApplyTo` ("2" |> "3" |> "4" |> "5"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` ("1" |> "2" |> "3" |> "4") m `shouldApplyTimes` c `to` ("2" |> "3" |> "4" |> "5"), verifyApplyFailed = \m -> m `shouldApplyTo` ("1" |> "2" |> "3" |> "x") } mockTest Fixture { name = "arity = 5", create = createMock [ "1" |> "2" |> "3" |> "4" |> "5" |> True, "2" |> "3" |> "4" |> "5" |> "6" |> False ], expected = [ True, False ], apply = \m -> [ stubFn m "1" "2" "3" "4" "5", stubFn m "2" "3" "4" "5" "6" ], applyFailed = Just \m -> [stubFn m "1" "2" "3" "4" "x"], verifyApply = \m -> do m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5") m `shouldApplyTo` ("2" |> "3" |> "4" |> "5" |> "6"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` ("1" |> "2" |> "3" |> "4" |> "5") m `shouldApplyTimes` c `to` ("2" |> "3" |> "4" |> "5" |> "6"), verifyApplyFailed = \m -> m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "x") } mockTest Fixture { name = "arity = 6", create = createMock [ "1" |> "2" |> "3" |> "4" |> "5" |> "6" |> True, "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> False ], expected = [ True, False ], apply = \m -> [ stubFn m "1" "2" "3" "4" "5" "6", stubFn m "2" "3" "4" "5" "6" "7" ], applyFailed = Just \m -> [stubFn m "1" "2" "3" "4" "5" "x"], verifyApply = \m -> do m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5" |> "6") m `shouldApplyTo` ("2" |> "3" |> "4" |> "5" |> "6" |> "7"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` ("1" |> "2" |> "3" |> "4" |> "5" |> "6") m `shouldApplyTimes` c `to` ("2" |> "3" |> "4" |> "5" |> "6" |> "7"), verifyApplyFailed = \m -> m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5" |> "x") } mockTest Fixture { name = "arity = 7", create = createMock [ "1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> True, "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> False ], expected = [ True, False ], apply = \m -> [ stubFn m "1" "2" "3" "4" "5" "6" "7", stubFn m "2" "3" "4" "5" "6" "7" "8" ], applyFailed = Just \m -> [stubFn m "1" "2" "3" "4" "5" "6" "x"], verifyApply = \m -> do m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7") m `shouldApplyTo` ("2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7") m `shouldApplyTimes` c `to` ("2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8"), verifyApplyFailed = \m -> m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "x") } mockTest Fixture { name = "arity = 8", create = createMock [ "1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> True, "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9" |> False ], expected = [ True, False ], apply = \m -> [ stubFn m "1" "2" "3" "4" "5" "6" "7" "8", stubFn m "2" "3" "4" "5" "6" "7" "8" "9" ], applyFailed = Just \m -> [stubFn m "1" "2" "3" "4" "5" "6" "7" "x"], verifyApply = \m -> do m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8") m `shouldApplyTo` ("2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8") m `shouldApplyTimes` c `to` ("2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9"), verifyApplyFailed = \m -> m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "x") } mockTest Fixture { name = "arity = 9", create = createMock [ "1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9" |> True, "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9" |> "10" |> False ], expected = [ True, False ], apply = \m -> [ stubFn m "1" "2" "3" "4" "5" "6" "7" "8" "9", stubFn m "2" "3" "4" "5" "6" "7" "8" "9" "10" ], applyFailed = Just \m -> [stubFn m "1" "2" "3" "4" "5" "6" "7" "8" "x"], verifyApply = \m -> do m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9") m `shouldApplyTo` ("2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9" |> "10"), verifyApplyCount = \m c -> do m `shouldApplyTimes` c `to` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9") m `shouldApplyTimes` c `to` ("2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9" |> "10"), verifyApplyFailed = \m -> m `shouldApplyTo` ("1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "x") } describe "Order Verification" do describe "exactly sequential order." do mockOrderTest VerifyOrderFixture { name = "arity = 1", create = createMock $ any |> (), apply = \m -> do evaluate $ stubFn m "a" evaluate $ stubFn m "b" evaluate $ stubFn m "c", verifyApply = \m -> m `shouldApplyInOrder` [ "a", "b", "c" ], verifyApplyFailed = \m -> m `shouldApplyInOrder` [ "a", "b", "b" ] } mockOrderTest VerifyOrderFixture { name = "arity = 9", create = createMock $ any |> any |> any |> any |> any |> any |> any |> any |> (), apply = \m -> do evaluate $ stubFn m "1" "2" "3" "4" "5" "6" "7" "8" evaluate $ stubFn m "2" "3" "4" "5" "6" "7" "8" "9" evaluate $ stubFn m "3" "4" "5" "6" "7" "8" "9" "0", verifyApply = \m -> m `shouldApplyInOrder` [ "1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8", "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9", "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9" |> "0" ], verifyApplyFailed = \m -> m `shouldApplyInOrder` [ "1" |> "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "x", "2" |> "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "y", "3" |> "4" |> "5" |> "6" |> "7" |> "8" |> "9" |> "z" ] } mockOrderTest VerifyOrderFixture { name = "number of function calls doesn't match the number of params.", create = createMock $ any |> (), apply = \m -> do evaluate $ stubFn m "a" pure (), verifyApply = \m -> m `shouldApplyInOrder` [ "a" ], verifyApplyFailed = \m -> m `shouldApplyInOrder` [ "a", "b" ] } describe "partially sequential order." do mockOrderTest VerifyOrderFixture { name = "arity = 1", create = createMock $ any |> (), apply = \m -> do evaluate $ stubFn m "a" evaluate $ stubFn m "b" evaluate $ stubFn m "c" pure (), verifyApply = \m -> m `shouldApplyInPartialOrder` [ "a", "c" ], verifyApplyFailed = \m -> m `shouldApplyInPartialOrder` [ "b", "a" ] } mockOrderTest VerifyOrderFixture { name = "arity = 9", create = createMock $ any |> any |> any |> any |> any |> any |> any |> any |> any |> (), apply = \m -> do evaluate $ stubFn m "a" "" "" "" "" "" "" "" "" evaluate $ stubFn m "b" "" "" "" "" "" "" "" "" evaluate $ stubFn m "c" "" "" "" "" "" "" "" "" pure (), verifyApply = \m -> m `shouldApplyInPartialOrder` [ "a" |> "" |> "" |> "" |> "" |> "" |> "" |> "" |> "", "c" |> "" |> "" |> "" |> "" |> "" |> "" |> "" |> "" ], verifyApplyFailed = \m -> m `shouldApplyInPartialOrder` [ "b" |> "" |> "" |> "" |> "" |> "" |> "" |> "" |> "", "a" |> "" |> "" |> "" |> "" |> "" |> "" |> "" |> "" ] } mockOrderTest VerifyOrderFixture { name = "Uncalled value specified.", create = createMock $ any |> (), apply = \m -> do evaluate $ stubFn m "a" evaluate $ stubFn m "b" evaluate $ stubFn m "c" pure (), verifyApply = \m -> m `shouldApplyInPartialOrder` [ "b", "c" ], verifyApplyFailed = \m -> m `shouldApplyInPartialOrder` [ "a", "d" ] } mockOrderTest VerifyOrderFixture { name = "number of function calls doesn't match the number of params", create = createMock $ any |> (), apply = \m -> do evaluate $ stubFn m "a" pure (), verifyApply = \m -> m `shouldApplyInPartialOrder` [ "a" ], verifyApplyFailed = \m -> m `shouldApplyInPartialOrder` [ "a", "b" ] } describe "The number of times applied can also be verified by specifying conditions." do it "greater than equal" do m <- createMock $ "a" |> True evaluate $ stubFn m "a" evaluate $ stubFn m "a" evaluate $ stubFn m "a" m `shouldApplyTimesGreaterThanEqual` 3 `to` "a" it "less than equal" do m <- createMock $ "a" |> False evaluate $ stubFn m "a" evaluate $ stubFn m "a" evaluate $ stubFn m "a" m `shouldApplyTimesLessThanEqual` 3 `to` "a" it "greater than" do m <- createMock $ "a" |> True evaluate $ stubFn m "a" evaluate $ stubFn m "a" evaluate $ stubFn m "a" m `shouldApplyTimesGreaterThan` 2 `to` "a" it "less than" do m <- createMock $ "a" |> False evaluate $ stubFn m "a" evaluate $ stubFn m "a" evaluate $ stubFn m "a" m `shouldApplyTimesLessThan` 4 `to` "a" describe "Monad" do it "Return IO Monad." do m <- createMock $ "Article Id" |> pure @IO "Article Title" result <- stubFn m "Article Id" result `shouldBe` "Article Title" m `shouldApplyTo` "Article Id" describe "Appropriate message when a test fails." do describe "anonymous mock" do describe "apply" do it "simple mock" do m <- createMock $ "a" |> pure @IO True stubFn m "b" `shouldThrow` errorCall "Expected arguments were not applied to the function.\n\ \ expected: \"a\"\n\ \ but got: \"b\"" it "multi mock" do m <- createMock [ "aaa" |> (100 :: Int) |> pure @IO True, "bbb" |> (200 :: Int) |> pure @IO False ] stubFn m "aaa" 200 `shouldThrow` errorCall "Expected arguments were not applied to the function.\n\ \ expected one of the following:\n\ \ \"aaa\",100\n\ \ \"bbb\",200\n\ \ but got:\n\ \ \"aaa\",200" describe "verify" do it "simple mock verify" do m <- createMock $ any |> pure @IO True evaluate $ stubFn m "A" m `shouldApplyTo` "X" `shouldThrow` errorCall "Expected arguments were not applied to the function.\n\ \ expected: \"X\"\n\ \ but got: \"A\"" it "It has never been applied." do m <- createMock $ "X" |> pure @IO True m `shouldApplyTo` "X" `shouldThrow` errorCall "Expected arguments were not applied to the function.\n\ \ expected: \"X\"\n\ \ but got: It has never been applied" it "count" do m <- createMock $ any |> pure @IO True evaluate $ stubFn m "A" let e = "The expected argument was not applied the expected number of times to the function.\n\ \ expected: 2\n\ \ but got: 1" m `shouldApplyTimes` (2 :: Int) `to` "A" `shouldThrow` errorCall e it "verify sequence" do m <- createMock $ any |> pure @IO False evaluate $ stubFn m "B" evaluate $ stubFn m "C" evaluate $ stubFn m "A" let e = "Expected arguments were not applied to the function in the expected order.\n\ \ expected 1st applied: \"A\"\n\ \ but got 1st applied: \"B\"\n\ \ expected 2nd applied: \"B\"\n\ \ but got 2nd applied: \"C\"\n\ \ expected 3rd applied: \"C\"\n\ \ but got 3rd applied: \"A\"" m `shouldApplyInOrder` ["A", "B", "C"] `shouldThrow` errorCall e it "verify sequence (count mismatch)" do m <- createMock $ any |> True evaluate $ stubFn m "B" evaluate $ stubFn m "C" let e = "Expected arguments were not applied to the function in the expected order (count mismatch).\n\ \ expected: 3\n\ \ but got: 2" m `shouldApplyInOrder` ["A", "B", "C"] `shouldThrow` errorCall e it "verify partially sequence" do m <- createMock $ any |> True evaluate $ stubFn m "B" evaluate $ stubFn m "A" let e = "Expected arguments were not applied to the function in the expected order.\n\ \ expected order:\n\ \ \"A\"\n\ \ \"C\"\n\ \ but got:\n\ \ \"B\"\n\ \ \"A\"" m `shouldApplyInPartialOrder` ["A", "C"] `shouldThrow` errorCall e it "verify partially sequence (count mismatch)" do m <- createMock $ any |> False evaluate $ stubFn m "B" let e = "Expected arguments were not applied to the function in the expected order (count mismatch).\n\ \ expected: 2\n\ \ but got: 1" m `shouldApplyInPartialOrder` ["A", "C"] `shouldThrow` errorCall e describe "named mock" do describe "aply" do it "simple mock" do m <- createNamedMock "mock function" $ "a" |> pure @IO () let e = "Expected arguments were not applied to the function `mock function`.\n\ \ expected: \"a\"\n\ \ but got: \"b\"" stubFn m "b" `shouldThrow` errorCall e it "multi mock" do m <- createNamedMock "mock function" [ "aaa" |> True |> pure @IO True, "bbb" |> False |> pure @IO False ] let e = "Expected arguments were not applied to the function `mock function`.\n\ \ expected one of the following:\n\ \ \"aaa\",True\n\ \ \"bbb\",False\n\ \ but got:\n\ \ \"aaa\",False" stubFn m "aaa" False `shouldThrow` errorCall e describe "verify" do it "simple mock verify" do m <- createNamedMock "mock function" $ any |> pure @IO () evaluate $ stubFn m "A" let e = "Expected arguments were not applied to the function `mock function`.\n\ \ expected: \"X\"\n\ \ but got: \"A\"" m `shouldApplyTo` "X" `shouldThrow` errorCall e it "count" do m <- createNamedMock "mock function" $ any |> pure @IO () evaluate $ stubFn m "A" let e = "The expected argument was not applied the expected number of times to the function `mock function`.\n\ \ expected: 2\n\ \ but got: 1" m `shouldApplyTimes` (2 :: Int) `to` "A" `shouldThrow` errorCall e it "verify sequence" do m <- createNamedMock "mock function" $ any |> pure @IO () evaluate $ stubFn m "B" evaluate $ stubFn m "C" evaluate $ stubFn m "A" let e = "Expected arguments were not applied to the function `mock function` in the expected order.\n\ \ expected 1st applied: \"A\"\n\ \ but got 1st applied: \"B\"\n\ \ expected 2nd applied: \"B\"\n\ \ but got 2nd applied: \"C\"\n\ \ expected 3rd applied: \"C\"\n\ \ but got 3rd applied: \"A\"" m `shouldApplyInOrder` ["A", "B", "C"] `shouldThrow` errorCall e it "verify sequence (count mismatch)" do m <- createNamedMock "createStubFnc" $ any |> pure @IO () evaluate $ stubFn m "B" evaluate $ stubFn m "C" let e = "Expected arguments were not applied to the function `createStubFnc` in the expected order (count mismatch).\n\ \ expected: 3\n\ \ but got: 2" m `shouldApplyInOrder` ["A", "B", "C"] `shouldThrow` errorCall e it "verify partially sequence" do m <- createNamedMock "mock function" $ any |> pure @IO () evaluate $ stubFn m "B" evaluate $ stubFn m "A" let e = "Expected arguments were not applied to the function `mock function` in the expected order.\n\ \ expected order:\n\ \ \"A\"\n\ \ \"C\"\n\ \ but got:\n\ \ \"B\"\n\ \ \"A\"" m `shouldApplyInPartialOrder` ["A", "C"] `shouldThrow` errorCall e it "verify partially sequence (count mismatch)" do m <- createNamedMock "createStubFnc" $ any |> pure @IO () evaluate $ stubFn m "B" let e = "Expected arguments were not applied to the function `createStubFnc` in the expected order (count mismatch).\n\ \ expected: 2\n\ \ but got: 1" m `shouldApplyInPartialOrder` ["A", "C"] `shouldThrow` errorCall e describe "use expectation" do it "expectByExpr" do f <- createStubFn $ $(expectByExpr [|\x -> x == "y" || x == "z"|]) |> True f "y" `shouldBe` True describe "repeatable" do it "arity = 1" do f <- createStubFn [ "a" |> True, "b" |> False, "a" |> False, "b" |> True ] v1 <- evaluate $ f "a" v2 <- evaluate $ f "a" v3 <- evaluate $ f "b" v4 <- evaluate $ f "b" v1 `shouldBe` True v2 `shouldBe` False v3 `shouldBe` False v4 `shouldBe` True it "arity = 2" do f <- createStubFn [ "a" |> "b" |> (0 :: Int), "a" |> "c" |> (1 :: Int), "a" |> "b" |> (2 :: Int), "a" |> "c" |> (3 :: Int) ] v1 <- evaluate $ f "a" "b" v2 <- evaluate $ f "a" "b" v3 <- evaluate $ f "a" "c" v4 <- evaluate $ f "a" "c" v5 <- evaluate $ f "a" "b" v1 `shouldBe` (0 :: Int) v2 `shouldBe` (2 :: Int) v3 `shouldBe` (1 :: Int) v4 `shouldBe` (3 :: Int) v5 `shouldBe` (2 :: Int) data Fixture mock r = Fixture { name :: String, create :: IO mock, apply :: mock -> r, applyFailed :: Maybe (mock -> r), expected :: r, verifyApply :: mock -> IO (), verifyApplyFailed :: mock -> IO (), verifyApplyCount :: mock -> Int -> IO () } data VerifyOrderFixture mock r = VerifyOrderFixture { name :: String, create :: IO mock, apply :: mock -> IO r, verifyApply :: mock -> IO (), verifyApplyFailed :: mock -> IO () } class Eval a where evaluate :: a -> IO a instance Eval [a] where evaluate v = do mapM_ (\x -> E.evaluate (x `seq` ())) v pure v instance {-# OVERLAPPABLE #-} Eval a where evaluate = E.evaluate -- mock test template mockTest :: (Eq r, Show r, Eval r) => Fixture mock r -> SpecWith (Arg Expectation) mockTest f = describe f.name do it "Expected argument is applied, the expected value is returned." do m <- f.create f.apply m `shouldBe` f.expected it "Unexpected argument is applied, an exception is thrown." do f.applyFailed & maybe mempty \func -> do m <- f.create evaluate (func m) `shouldThrow` anyErrorCall it "Expected arguments are applied, the verification succeeds." do m <- f.create evaluate $ f.apply m f.verifyApply m it "Unexpected arguments are applied, the verification fails." do m <- f.create evaluate $ f.apply m f.verifyApplyFailed m `shouldThrow` anyErrorCall it "The number of times a function has been applied can be verification (0 times)." do m <- f.create f.verifyApplyCount m 0 it "The number of times a function has been applied can be verification (3 times)." do m <- f.create evaluate $ f.apply m evaluate $ f.apply m evaluate $ f.apply m f.verifyApplyCount m 3 it "Fails to verification the number of times it has been applied, an exception is thrown." do m <- f.create evaluate $ f.apply m f.verifyApplyCount m 3 `shouldThrow` anyErrorCall mockOrderTest :: VerifyOrderFixture mock r -> SpecWith (Arg Expectation) mockOrderTest f = describe f.name do it "If the functions are applied in the expected order, the verification succeeds." do m <- f.create f.apply m f.verifyApply m it "If the functions are not applied in the expected order, verification fails." do m <- f.create f.apply m f.verifyApplyFailed m `shouldThrow` anyErrorCall