packages feed

mockcat-0.2.0.0: test/Test/MockCat/MockSpec.hs

{-# 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 "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