如何为 ANSII 转义编码字符串解析器编写 Quickcheck 属性?

问题描述 投票:0回答:1

请考虑以下代码:

-- Represents a parsing result of an ANSII coded string.
data Slice = Slice
  { text :: String,
    color :: Color
  }

newtype Color = Color
  { string :: String
  }

-- A function that receives a string with ANSII esacpe codes and returns a list of slices.
categorize:: String -> [Slice]
categorize codedString = ...

现在,我想为

categorize
函数编写一个快速检查属性。 我有这样的想法:

-- A quickcheck generator for ANSII coded strings.
ansiEscapeStrings :: Gen String
ansiEscapeStrings = ...

main =
  verboseCheck $
    forAll
      ansiEscapeStrings
      (\codedString -> categorize codedString == WHAT_GOES_HERE)

我的问题是什么代替

WHAT_GOES_HERE

提前致谢。

unit-testing haskell quickcheck
1个回答
0
投票

使用 QuickCheck,您应该确定一些您认为应该适用于所有可能的输入/输出对的规则。如果您的输入概念只是“我传递给函数的字符串”而输出是“函数返回的切片”,则很难做到这一点。在这个抽象级别上,您真正可以编写的唯一规则是“函数应该生成由输入字符串表示的切片”——当然,您不能直接测试它,因为如果您从输入中进行了已知良好的转换输出你只是用它来代替。

相反,尝试在更细粒度的层面上思考。 给定输入的某些属性,您认为这个函数应该以哪些方式表现?这是我能想到的一些。

  1. 一个空的输入应该产生一个空的输出。
  2. 输出中
    text
    字符串的长度总和不应大于输入的大小。
  3. 输出中
    text
    字符串之一中出现的所有字符都应出现在输入中的某处。
  4. 如果输入包含
    n
    变色序列,则输出中应该有
    n
    切片。还是
    n-1
    片?您打算如何表示“任何颜色变化序列之前的文本”?如果连续有两个颜色变化序列,它们之间没有文本怎么办?考虑到要测试的属性,我们已经在设计中找到了重要的边缘案例。

这些属性需要不同级别的精度来测试它们。对于 (1),您甚至不需要 QuickCheck:您只需对单个空输入进行单元测试即可。对于 (2) 和 (3),您可以只传递一个任意字符串作为输入并比较输入和输出数据。但是 QuickCheck 的任意字符串生成器可能不会生成许多其中包含有意义的 ANSI 转义序列的字符串。所以你可能想要定义

stringWithEscapeSequences :: Gen String
或类似的东西,以确保你的测试输入是有趣的。

但是 (4) 更复杂。您 可以 将任意字符串作为输入,扫描它以查找转义序列,然后将其与

categorize
生成的切片数进行比较。但这需要在测试中包含大量函数的实现,并且函数中的错误很容易被测试中的错误所反映。一种不那么脆弱的方法是为测试用例编写一个数据类型,并为该类型编写一个生成器,这样您就可以提前知道每个测试用例需要多少个切片。有点像

data SliceCountTestCase = SliceCountTestCase 
  { numSlices :: Int, input :: String }
  deriving (Show, Eq)

instance Arbitrary SliceCountTestCase where
  arbitrary = do
    slices <- listOf slice
    pure $ SliceCountTestCase (length slices) (serialize =<< slices)
    where slice = (,) <$> color <*> listOf nonEscapeCharacter
          serialize (c, body) = escapeSequence c ++ body

prop_sliceCountMatches :: SliceCountTestCase -> Bool
prop_sliceCountMatches (SliceCountTestCase n s) = length (categorize s) == n

该示例中有许多地方需要您填写:

color
escapeSequence
nonEscapeCharacter
都是您需要根据您的领域知识编写的生成器。

您可以使用许多相同的组合器设计另一个类似的属性:给定切片列表作为输入,您应该能够将其编码为字符串,并且该字符串上的

categorize
应该会返回与开始时相同的结果与.

© www.soinside.com 2019 - 2024. All rights reserved.