在Haskell中为不同类型编写测试用例

时间:2017-02-20 13:53:46

标签: unit-testing haskell testing

我已经创建了一个库,可以创建"列出像"序列,其中实现了许多Prelude样式函数。我想为此编写一些测试用例,以确保我的库产生正确的输出,我认为最简单的方法是编写一些函数,将结果转换为列表并将它们与Prelude结果进行比较。让我们说我们得到了这个:

import qualified MyLibrary as ML
import qualified Prelude as P

例如,我可能需要以下测试用例:

P.take 5 (P.enumFrom 1) == toList (ML.take 5 (ML.enumFrom 1))

请注意ML.enumFrom不输出列表,而是输出自己的数据类型。

以上工作正常,但请注意我是如何重复自己的#34; (TM)。我必须确保左侧和右侧相同,否则我的测试用例是错误的。

有没有一种很好的方式来编写这样的测试用例,所以我不必重复自己?

1 个答案:

答案 0 :(得分:1)

P.takeML.take等只是看起来相似的第一个问题 - 实际上它们是完全无关的函数,编译器对它们的常见行为一无所知。因此,正如@ jd823592提出的那样,我们需要使用类型类对它们进行分组(我使用了一个简单的newtype包装器,因此该示例可以编译):

import Prelude hiding (take, enumFrom)
import qualified Prelude as P (take, enumFrom)

newtype MySeq a = MySeq [a]

class Sequence s where
    take :: Int -> s a -> s a
    enumFrom :: Enum a => a -> s a
    toList :: s a -> [a]

instance Sequence MySeq where
    take n (MySeq xs) = MySeq (P.take n xs)
    enumFrom n = MySeq (P.enumFrom n)
    toList (MySeq xs) = xs

instance Sequence [] where
    take = P.take
    enumFrom = P.enumFrom
    toList = id

然后我们将尝试使用类定义中现在统一的函数来定义一些测试。它们可能只生成任何类型的Sequence,然后我们将强制它们生成显式类型。

test1 = doTest (take 5 $ enumFrom 1) -- the part in brackets is polymorphic

doTest :: (Eq a, Sequence s) => s a -> Bool
doTest test = ???

现在第二个问题是我们将多态函数作为参数传递,然后需要使用不同的类型参数(在这种情况下为[a]MySeq a)对其进行实例化。在标准的Haskell 2010中,这是不可能的,但我们可以利用Rank2 (or RankN) extension

{-# LANGUAGE Rank2Types #-}

<...>

doTest :: forall a . Eq a => (forall s . Sequence s => s a) -> Bool
doTest test = (test `asTypeOf` dummy1) == toList (test `asTypeOf` dummy2) where
    dummy1 :: Eq a => [a]
    dummy1 = undefined
    dummy2 :: Eq a => MySeq a
    dummy2 = undefined

这个解决方案有点笨拙,但仍然有效。请随时改进。