【问题标题】:testing functions that return a Maybe Monad测试返回 Maybe Monad 的函数
【发布时间】:2016-01-16 12:16:01
【问题描述】:

假设我有一个函数:

safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead xs = Just $ head xs

还有一个测试:

describe "Example.safeHead" $ do
  it "returns the head" $ do
    safeHead [1,2,3] `shouldBe` Just 1

  it "returns Nothing for an empty list" $
    safeHead [] `shouldBe` Nothing

然而这会产生:

No instance for (Eq a0) arising from a use of ‘shouldBe’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
  instance (Eq a, Eq b) => Eq (Either a b)
    -- Defined in ‘Data.Either’
  instance forall (k :: BOX) (s :: k). Eq (Data.Proxy.Proxy s)
    -- Defined in ‘Data.Proxy’
  instance (GHC.Arr.Ix i, Eq e) => Eq (GHC.Arr.Array i e)
    -- Defined in ‘GHC.Arr’
  ...plus 88 others
In the second argument of ‘($)’, namely
  ‘safeHead [] `shouldBe` Nothing’
In a stmt of a 'do' block:
  it "returns Nothing for an empty list"
  $ safeHead [] `shouldBe` Nothing
In the second argument of ‘($)’, namely
  ‘do { it "returns the head"
        $ do { safeHead [...] `shouldBe` Just 1 };
        it "returns Nothing for an empty list"
        $ safeHead [] `shouldBe` Nothing }’

为什么?我该如何解决?

【问题讨论】:

  • safeHead [] `shouldBe` Nothing 的类型不明确,因为没有什么可以告诉编译器在比较列表元素时使用哪种元素类型(当然,没关系,因为没有元素,但编译器不知道)。您可以通过提供显式类型来修复它:safeHead [] `shouldBe` (Nothing :: Maybe Int)

标签: haskell testing monads assertions hspec


【解决方案1】:

正如 user2407038 评论的那样,编译器不知道如何实例化 a。他提出的修复可能是最好的——你应该明确指定a的类型。

但为了完整起见,我想指出,还有其他解决方案,extended default rules

{-# LANGUAGE ExtendedDefaultRules #-}

describe "Example.safeHead" $ do
  it "returns the head" $ do
    safeHead [1,2,3] `shouldBe` Just 1

  it "returns Nothing for an empty list" $
    safeHead [] `shouldBe` Nothing

扩展修改the standard defaulting rules 以包含更多案例,例如Eq 类型类。

补充:经过一番思考,我重新考虑了我的答案。多态函数的单元测试结果不应依赖于实例化类型变量的特定方式。那么扩展的默认规则可能是测试中的正确方法吗?我从来没有使用过真正的代码,所以我不能肯定,但绝对值得考虑。

【讨论】:

  • 非常有趣。我很想知道这个解决方案在给出类型规范方面的相对优缺点是什么?还是字面意思是“优点:打字少,缺点:类型更松散”?
  • @AbrahamP 基本上是的,少打字可能是唯一的优点:)
【解决方案2】:
shouldBe                :: (Eq a, Show a) => a -> a -> Expectation
safeHead                ::         [a] ->    Maybe a
[]                      ::         [a]
safeHead []             ::                   Maybe a
Nothing                 ::                   Maybe a
shouldBe (safeHead [])  :: (Eq a, Show a) => Maybe a -> Expectation

shouldBe (safeHead []) Nothing ::                       Expectation -- but what's `a`?

如您所见,a 完全模棱两可。它可以是具有ShowEq 实例的任何类型。这也是您的错误消息的一部分:

没有因使用“shouldBe”而产生 (Eq a0) 的实例 类型变量“a0”不明确

所以选择一个:

it "returns Nothing for an empty list" $
    safeHead [] `shouldBe` (Nothing :: Maybe ())

当您使用它时,使用 QuickCheck 来验证 safeHead 是否像 head 一样工作:

it "returns Just (head xs) for a non-empty list" $ property $ \(NonEmpty xs) ->
    safeHead xs `shouldBe` (Just (head xs) :: Maybe Integer)

【讨论】:

    【解决方案3】:

    这段代码测试了一个返回“Just”值的 Maybe Monad:

         import Test.Tasty
         import Test.Tasty.HUnit
         import Data.List         
    
         main = defaultMain tests
    
         tests = testGroup "Tests uncons form Data.List"
    
         [testCase "Show uncons [1,2,3,4] = (1, [2,3,4])" $
         Just (1,[2,3,4]) @=? (uncons [1,2,3,4])]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-26
      • 1970-01-01
      • 2015-05-21
      • 1970-01-01
      • 2017-02-04
      相关资源
      最近更新 更多