packages feed

mockcat 0.1.0.0 → 0.2.0.0

raw patch · 8 files changed

+291/−48 lines, 8 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Test.MockCat.Mock: createNamedStubFun :: MockBuilder params fun verifyParams => MonadIO m => String -> params -> m fun
+ Test.MockCat.AssociationList: (!?) :: Eq k => AssociationList k a -> k -> Maybe a
+ Test.MockCat.AssociationList: empty :: AssociationList k a
+ Test.MockCat.AssociationList: insert :: Eq k => k -> a -> AssociationList k a -> AssociationList k a
+ Test.MockCat.AssociationList: lookup :: Eq k => k -> AssociationList k a -> Maybe a
+ Test.MockCat.AssociationList: member :: Eq k => k -> AssociationList k a -> Bool
+ Test.MockCat.AssociationList: type AssociationList k a = [(k, a)]
+ Test.MockCat.AssociationList: update :: Eq k => (a -> a) -> k -> AssociationList k a -> AssociationList k a
+ Test.MockCat.Mock: createNamedStubFn :: MockBuilder params fun verifyParams => MonadIO m => String -> params -> m fun

Files

README.md view
@@ -179,6 +179,31 @@     f "b" `shouldBe` "return y" ``` +## Stub Function That Returns Different Values for the Same Arguments+When applying a list of x |> y pairs to the `createStubFn` function, you can create a stub function that returns different values for the same arguments by ensuring the return values differ for the same input arguments.+```haskell+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE TypeApplications #-}+import Test.Hspec+import Test.MockCat+import GHC.IO (evaluate)++spec :: Spec+spec = do+  it "Return different values for the same argument" do+    f <- createStubFn [+        "arg" |> "x",+        "arg" |> "y"+      ]+    -- Do not allow optimization to remove duplicates.+    v1 <- evaluate $ f "arg"+    v2 <- evaluate $ f "arg"+    v3 <- evaluate $ f "arg"+    v1 `shouldBe` "x"+    v2 `shouldBe` "y"+    v3 `shouldBe` "y" -- After the second time, “y” is returned.+```+ # Verification ## Verify if the Expected Arguments were Applied You can verify if the expected arguments were applied using the `shouldApplyTo` function.@@ -202,6 +227,30 @@     -- verify     mock `shouldApplyTo` "value" ```+### Note+The recording of the application of arguments is done at the time the return value of the stub function is evaluated.  +Therefore, verification must be done after evaluation.+```haskell+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE TypeApplications #-}+import Test.Hspec+import Test.MockCat++spec :: Spec+spec = do+  it "Verification does not work" do+    mock <- createMock $ "expect arg" |> "return value"+    -- Apply arguments to stub functions but do not evaluate values+    let _ = stubFn mock "expect arg"+    mock `shouldApplyTo` "expect arg"+```+```console+uncaught exception: ErrorCall+Expected arguments were not applied to the function.+  expected: "expect arg"+  but got: Never been called.+```+ ## Verify the Number of Times the Expected Arguments were Applied You can verify the number of times the expected arguments were applied using the `shouldApplyTimes` function. ```haskell
mockcat.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           mockcat-version:        0.1.0.0+version:        0.2.0.0 synopsis:       Simple mock function library for test in Haskell. description:    mockcat is simple mock library for test in Haskell.                 .@@ -41,6 +41,7 @@ library   exposed-modules:       Test.MockCat+      Test.MockCat.AssociationList       Test.MockCat.Cons       Test.MockCat.Mock       Test.MockCat.Param@@ -61,6 +62,7 @@   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:+      Test.MockCat.AssociationListSpec       Test.MockCat.ConsSpec       Test.MockCat.ExampleSpec       Test.MockCat.MockSpec
+ src/Test/MockCat/AssociationList.hs view
@@ -0,0 +1,34 @@+module Test.MockCat.AssociationList + (AssociationList, empty, insert, lookup, member, (!?), update) where++import Prelude hiding (lookup)+import Data.Maybe (isJust)++type AssociationList k a = [(k, a)]++empty :: AssociationList k a+empty = []++insert :: Eq k => k -> a -> AssociationList k a -> AssociationList k a+insert key value [] = [(key, value)]+insert key value ((k, v) : xs)+  | key == k  = (key, value) : xs+  | otherwise = (k, v) : insert key value xs++lookup :: Eq k => k -> AssociationList k a -> Maybe a+lookup _ [] = Nothing+lookup key ((k, a) : xs)+  | key == k  = Just a+  | otherwise = lookup key xs++member :: Eq k => k -> AssociationList k a -> Bool+member k list = isJust (lookup k list)++(!?) :: Eq k => AssociationList k a -> k -> Maybe a+(!?) = flip lookup++update :: Eq k => (a -> a) -> k -> AssociationList k a -> AssociationList k a+update f key list =+  case list !? key of+    Just value -> insert key (f value) list+    Nothing    -> list
src/Test/MockCat/Mock.hs view
@@ -19,8 +19,8 @@   ( createMock,     createNamedMock,     createStubFn,-    createNamedStubFun,-    stubFn,       +    createNamedStubFn,+    stubFn,     shouldApplyTo,     shouldApplyTimes,     shouldApplyInOrder,@@ -39,19 +39,21 @@ import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Function ((&)) import Data.IORef (IORef, modifyIORef', newIORef, readIORef)-import Data.List (elemIndex, find, intercalate)+import Data.List (elemIndex, intercalate) import Data.Maybe import Data.Text (pack, replace, unpack) import GHC.IO (unsafePerformIO) import Test.MockCat.Cons import Test.MockCat.Param import Test.MockCat.ParamDivider+import Test.MockCat.AssociationList (AssociationList, lookup, update, insert, empty, member)+import Prelude hiding (lookup)  data Mock fun params = Mock (Maybe MockName) fun (Verifier params)  type MockName = String -newtype Verifier params = Verifier (IORef (AppliedParamsList params))+newtype Verifier params = Verifier (IORef (AppliedRecord params))  {- | Create a mock. From this mock, you can generate stub functions and verify the functions.@@ -121,14 +123,15 @@   m fun createStubFn params = stubFn <$> createMock params + -- | Create a named stub function.-createNamedStubFun ::+createNamedStubFn ::   MockBuilder params fun verifyParams =>   MonadIO m =>   String ->   params ->   m fun-createNamedStubFun name params = stubFn <$> createNamedMock name params+createNamedStubFn name params = stubFn <$> createNamedMock name params  -- | Class for creating a mock corresponding to the parameter. class MockBuilder params fun verifyParams | params -> fun, params -> verifyParams where@@ -144,7 +147,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e :> Param f :> Param g :> Param h :> Param i)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock       name       s@@ -160,7 +163,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e :> Param f :> Param g :> Param h)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock       name       s@@ -176,7 +179,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e :> Param f :> Param g)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock       name       s@@ -192,7 +195,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e :> Param f)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 e2 f2 -> unsafePerformIO $ extractReturnValueWithValidate name params (p a2 :> p b2 :> p c2 :> p d2 :> p e2 :> p f2) s)  instance@@ -203,7 +206,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 e2 -> unsafePerformIO $ extractReturnValueWithValidate name params (p a2 :> p b2 :> p c2 :> p d2 :> p e2) s)  instance@@ -214,7 +217,7 @@     (Param a :> Param b :> Param c :> Param d)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 -> unsafePerformIO $ extractReturnValueWithValidate name params (p a2 :> p b2 :> p c2 :> p d2) s)  instance@@ -222,7 +225,7 @@   MockBuilder (Param a :> Param b :> Param c :> Param r) (a -> b -> c -> r) (Param a :> Param b :> Param c)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 -> unsafePerformIO $ extractReturnValueWithValidate name params (p a2 :> p b2 :> p c2) s)  instance@@ -230,7 +233,7 @@   MockBuilder (Param a :> Param b :> Param r) (a -> b -> r) (Param a :> Param b)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 -> unsafePerformIO $ extractReturnValueWithValidate name params (p a2 :> p b2) s)  instance@@ -238,7 +241,7 @@   MockBuilder (Param a :> Param r) (a -> r) (Param a)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 -> unsafePerformIO $ extractReturnValueWithValidate name params (p a2) s)  instance@@ -249,7 +252,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e :> Param f :> Param g :> Param h :> Param i)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 e2 f2 g2 h2 i2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2 :> p b2 :> p c2 :> p d2 :> p e2 :> p f2 :> p g2 :> p h2 :> p i2) s)  instance@@ -260,7 +263,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e :> Param f :> Param g :> Param h)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 e2 f2 g2 h2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2 :> p b2 :> p c2 :> p d2 :> p e2 :> p f2 :> p g2 :> p h2) s)  instance@@ -271,7 +274,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e :> Param f :> Param g)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 e2 f2 g2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2 :> p b2 :> p c2 :> p d2 :> p e2 :> p f2 :> p g2) s)  instance@@ -282,7 +285,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e :> Param f)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 e2 f2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2 :> p b2 :> p c2 :> p d2 :> p e2 :> p f2) s)  instance@@ -293,7 +296,7 @@     (Param a :> Param b :> Param c :> Param d :> Param e)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 e2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2 :> p b2 :> p c2 :> p d2 :> p e2) s)  instance@@ -304,7 +307,7 @@     (Param a :> Param b :> Param c :> Param d)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 d2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2 :> p b2 :> p c2 :> p d2) s)  instance@@ -315,7 +318,7 @@     (Param a :> Param b :> Param c)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 c2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2 :> p b2 :> p c2) s)  instance@@ -323,7 +326,7 @@   MockBuilder [Param a :> Param b :> Param r] (a -> b -> r) (Param a :> Param b)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 b2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2 :> p b2) s)  instance@@ -331,7 +334,7 @@   MockBuilder [Param a :> Param r] (a -> r) (Param a)   where   build name params = do-    s <- liftIO $ newIORef ([] :: AppliedParamsList params)+    s <- liftIO $ newIORef appliedRecord     makeMock name s (\a2 -> unsafePerformIO $ findReturnValueWithStore name params (p a2) s)  -- ------@@ -339,11 +342,9 @@ p :: a -> Param a p = param -makeMock :: MonadIO m => Maybe MockName -> IORef (AppliedParamsList params) -> fun -> m (Mock fun params)+makeMock :: MonadIO m => Maybe MockName -> IORef (AppliedRecord params) -> fun -> m (Mock fun params) makeMock name l fn = pure $ Mock name fn (Verifier l) -type AppliedParamsList params = [params]- extractReturnValueWithValidate ::   ParamDivider params args (Param r) =>   Eq args =>@@ -351,7 +352,7 @@   Maybe MockName ->   params ->   args ->-  IORef (AppliedParamsList args) ->+  IORef (AppliedRecord args) ->   IO r extractReturnValueWithValidate name params inputParams s = do   validateWithStoreParams name s (args params) inputParams@@ -364,12 +365,12 @@   Maybe MockName ->   AppliedParamsList params ->   args ->-  IORef (AppliedParamsList args) ->+  IORef (AppliedRecord args) ->   IO r findReturnValueWithStore name paramsList inputParams ref = do-  modifyIORef' ref (++ [inputParams])+  appendAppliedParams ref inputParams   let expectedArgs = args <$> paramsList-      r = findReturnValue paramsList inputParams+  r <- findReturnValue paramsList inputParams ref   maybe     (errorWithoutStackTrace $ messageForMultiMock name expectedArgs inputParams)     pure@@ -380,15 +381,22 @@   ParamDivider params args (Param r) =>   AppliedParamsList params ->   args ->-  Maybe r-findReturnValue paramsList inputParams = do-  find (\params -> args params == inputParams) paramsList-    >>= \params -> pure $ returnValue params+  IORef (AppliedRecord args) ->+  IO (Maybe r)+findReturnValue paramsList inputParams ref = do+  let matchedParams = filter (\params -> args params == inputParams) paramsList+  case matchedParams of+    [] -> pure Nothing+    _ -> do+      count <- readAppliedCount ref inputParams+      let index = min count (length matchedParams - 1)+      incrementAppliedParamCount ref inputParams+      pure $ returnValue <$> safeIndex matchedParams index -validateWithStoreParams :: (Eq a, Show a) => Maybe MockName -> IORef (AppliedParamsList a) -> a -> a -> IO ()+validateWithStoreParams :: (Eq a, Show a) => Maybe MockName -> IORef (AppliedRecord a) -> a -> a -> IO () validateWithStoreParams name ref expected actual = do   validateParams name expected actual-  modifyIORef' ref (++ [actual])+  appendAppliedParams ref actual  validateParams :: (Eq a, Show a) => Maybe MockName -> a -> a -> IO () validateParams name expected actual =@@ -438,7 +446,7 @@  verify :: (Eq params, Show params) => Mock fun params -> VerifyMatchType params -> IO () verify (Mock name _ (Verifier ref)) matchType = do-  calledParamsList <- readIORef ref+  calledParamsList <- readAppliedParamsList ref   let result = doVerify name calledParamsList matchType   result & maybe (pure ()) (\(VerifyFailed msg) -> errorWithoutStackTrace msg) @@ -524,7 +532,7 @@  verifyCount :: Eq params => Mock fun params -> params -> CountVerifyMethod -> IO () verifyCount (Mock name _ (Verifier ref)) v method = do-  calledParamsList <- readIORef ref+  calledParamsList <- readAppliedParamsList ref   let callCount = length (filter (v ==) calledParamsList)   if compareCount method callCount     then pure ()@@ -583,17 +591,17 @@   [params] ->   IO () verifyOrder method (Mock name _ (Verifier ref)) matchers = do-  calledParamsList <- readIORef ref+  calledParamsList <- readAppliedParamsList ref   let result = doVerifyOrder method name calledParamsList matchers   result & maybe (pure ()) (\(VerifyFailed msg) -> errorWithoutStackTrace msg) -doVerifyOrder :: +doVerifyOrder ::   Eq a =>-  Show a => -  VerifyOrderMethod -> -  Maybe MockName -> -  AppliedParamsList a -> -  [a] -> +  Show a =>+  VerifyOrderMethod ->+  Maybe MockName ->+  AppliedParamsList a ->+  [a] ->   Maybe VerifyFailed doVerifyOrder ExactlySequence name calledValues expectedValues   | length calledValues /= length expectedValues = do@@ -721,3 +729,54 @@   a ->   IO () shouldApplyTimesLessThan m i = shouldApplyTimes m (LessThan i)++++type AppliedParamsList params = [params]+type AppliedParamsCounter params = AssociationList params Int++data AppliedRecord params = AppliedRecord {+  appliedParamsList :: AppliedParamsList params,+  appliedParamsCounter :: AppliedParamsCounter params+}++appliedRecord :: AppliedRecord params+appliedRecord = AppliedRecord {+  appliedParamsList = mempty,+  appliedParamsCounter = empty+}++readAppliedParamsList :: IORef (AppliedRecord params) -> IO (AppliedParamsList params)+readAppliedParamsList ref = do+  record <- readIORef ref+  pure $ appliedParamsList record++readAppliedCount :: Eq params => IORef (AppliedRecord params) -> params -> IO Int+readAppliedCount ref params = do+  record <- readIORef ref+  let count = appliedParamsCounter record+  pure $ fromMaybe 0 (lookup params count)++appendAppliedParams :: IORef (AppliedRecord params) -> params -> IO ()+appendAppliedParams ref inputParams = do+  modifyIORef' ref (\AppliedRecord {appliedParamsList, appliedParamsCounter} -> AppliedRecord {+    appliedParamsList = appliedParamsList ++ [inputParams],+    appliedParamsCounter = appliedParamsCounter+  })++incrementAppliedParamCount ::Eq params => IORef (AppliedRecord params) -> params -> IO ()+incrementAppliedParamCount ref inputParams = do+  modifyIORef' ref (\AppliedRecord {appliedParamsList, appliedParamsCounter} -> AppliedRecord {+    appliedParamsList = appliedParamsList,+    appliedParamsCounter = incrementCount inputParams appliedParamsCounter+  })++incrementCount :: Eq k => k -> AppliedParamsCounter k -> AppliedParamsCounter k+incrementCount key list =+  if member key list then update (+ 1) key list+  else insert key 1 list++safeIndex :: [a] -> Int -> Maybe a+safeIndex xs n+  | n < 0 = Nothing+  | otherwise = listToMaybe (drop n xs)
test/Spec.hs view
@@ -2,6 +2,7 @@ import Test.MockCat.MockSpec as Mock import Test.MockCat.ConsSpec as Cons import Test.MockCat.ParamSpec as Param+import Test.MockCat.AssociationListSpec as AssociationList import Test.MockCat.ExampleSpec as Example  main :: IO ()@@ -10,4 +11,5 @@     Cons.spec     Param.spec     Mock.spec+    AssociationList.spec     Example.spec
+ test/Test/MockCat/AssociationListSpec.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE BlockArguments #-}+{-# OPTIONS_GHC -Wno-type-defaults #-}+module Test.MockCat.AssociationListSpec (spec) where++import Test.Hspec+import Test.MockCat.AssociationList (AssociationList, empty, insert, update, (!?), member)++spec :: Spec+spec = do+  it "empty" do+    let e = empty :: AssociationList String Int+    e `shouldBe` []++  describe "insert" do+    it "not exist" do+      let l = empty :: AssociationList String Int+      insert "key" 100 l `shouldBe` [("key", 100)]++    it "exist" do+      let l = insert "key" 100 empty :: AssociationList String Int+      insert "key" 120 l `shouldBe` [("key", 120)]+  +  describe "update" do+    it "not exist" do+      let l = empty :: AssociationList String Int+      update (+ 20) "key" l `shouldBe` []++    it "exist" do+      let l = insert "key" 100 empty :: AssociationList String Int+      update (+ 20) "key" l `shouldBe` [("key", 120)]+  +  describe "lookup" do+    it "not exist" do+      let l = empty :: AssociationList String Int+      l !? "key" `shouldBe` Nothing++    it "exist" do+      let l = insert "key" 100 empty :: AssociationList String Int+      l !? "key" `shouldBe` Just 100++  describe "member" do+    it "not exist" do+      let l = empty :: AssociationList String Int+      member "key" l `shouldBe` False++    it "exist" do+      let l = insert "key" 100 empty :: AssociationList String Int+      member "key" l `shouldBe` True
test/Test/MockCat/ExampleSpec.hs view
@@ -7,6 +7,7 @@ import Test.Hspec import Test.MockCat import Prelude hiding (and, any, not, or)+import GHC.IO (evaluate)  spec :: Spec spec = do@@ -26,7 +27,7 @@     actual `shouldBe` ()    it "named stub" do-    f <- createNamedStubFun "named stub" $ "x" |> "y" |> True+    f <- createNamedStubFn "named stub" $ "x" |> "y" |> True     f "x" "y" `shouldBe` True    it "named mock" do@@ -86,3 +87,16 @@         ]     f "a" `shouldBe` "return x"     f "b" `shouldBe` "return y"++  it "Return different values for the same argument" do+    f <- createStubFn [+        "arg" |> "x",+        "arg" |> "y"+      ]+    -- Do not allow optimization to remove duplicates.+    v1 <- evaluate $ f "arg"+    v2 <- evaluate $ f "arg"+    v3 <- evaluate $ f "arg"+    v1 `shouldBe` "x"+    v2 `shouldBe` "y"+    v3 `shouldBe` "y" -- After the second time, “y” is returned.
test/Test/MockCat/MockSpec.hs view
@@ -793,6 +793,41 @@     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,