2017-08-01 86 views
3

我正在使用VisualStudio中的FsCheck和NUnit进行测试。自定义FsCheck输出

问题目前是:我设法生成随机图(用于测试一些图形功能),但是当一个测试失败,FsCheck吐出来的全图,所以它从字面上转储的记录,你的原始清单它不使用的ToString在那里看不到任何东西。

另外我不仅需要输入图形进行检查,而且还需要运行属性时创建的一些其他数据。

因此,如何能更改FsCheck的输出行为,以

  • 居然打电话给我的ToString方法输入图形
  • 输出的详细信息

当测试失败?编辑: 这是我目前的测试设置。

module GraphProperties 

open NUnit.Framework 
open FsCheck 
open FsCheck.NUnit 

let generateRandomGraph = 
    gen { 
     let graph: Graph<int,int> = Graph<_,_>.Empty() 
     // fill in random nodes and transitions... 
     return graph 
    } 

type MyGenerators = 
    static member Graph() = 
     {new Arbitrary<Graph<int,int>>() with 
      override this.Generator = generateRandomGraph 
      override this.Shrinker _ = Seq.empty } 

[<TestFixture>] 
type NUnitTest() = 
    [<Property(Arbitrary=[|typeof<MyGenerators>|], QuietOnSuccess = true)>] 
    member __.cloningDoesNotChangeTheGraph (originalGraph: Graph<int,int>) = 
     let newGraph = clone originalGraph 
     newGraph = originalGraph 
+2

你怎么做你的断言?即使使用NUnit来运行测试,也有不同的方法来断言。这将有助于提供一个示例测试,甚至是最后一行。 – TheQuickBrownFox

+0

我刚刚添加了一些代码示例。最难的部分是让发电机工作。对于属性本身,我得到随机输入,用这个输入调用待测方法并返回一个布尔值(在这种情况下为比较结果)。 –

+0

是'图<_,_>'是一个记录还是一个类?你能否提供(或许已被剥离)的定义?你看到的实际输出是什么? – TheQuickBrownFox

回答

1

FsCheck使用sprintf "%A"测试参数转换为字符串测试输出,所以你需要做的就是控制你的类型是如何由%A格式格式化。根据How do I customize output of a custom type using printf?,要做到这一点的方法是StructuredFormatDisplay attribute。该属性的值应该是格式为PreText {PropertyName} PostText的字符串,其中PropertyName应该是您的类型的属性(而不是函数!)。例如,假设您的树叶结构中包含一些复杂的信息,但对于您的测试,您只需要知道树叶的数量,而不是其中的内容。所以你会从这样的数据类型开始:

// Example 1 
type ComplicatedRecord = { ... } 
type Tree = 
    | Leaf of ComplicatedRecord 
    | Node of Tree list 
    with 
     member x.LeafCount = 
      match x with 
      | Leaf _ -> 1 
      | Node leaves -> leaves |> List.sumBy (fun x -> x.LeafCount) 
     override x.ToString() = 
      // For test output, we don't care about leaf data, just count 
      match x with 
      | Leaf -> "Tree with a total of 1 leaf" 
      | Node -> sprintf "Tree with a total of %d leaves" x.LeafCount 

现在,到目前为止,这不是你想要的。这种类型的确不是而是具有声明的自定义%A格式,所以FsCheck(以及其他任何使用sprintf "%A"来格式化的格式)将最终输出树的整个复杂结构及其所有不相关的测试叶数据。为了让FsCheck输出你想看到的内容,你需要设置一个属性,而不是一个函数(ToString不能用于这个目的),它会输出你想看到的内容。例如: -

// Example 2 
type ComplicatedRecord = { ... } 
[<StructuredFormatDisplay("{LeafCountAsString}")>] 
type Tree = 
    | Leaf of ComplicatedRecord 
    | Node of Tree list 
    with 
     member x.LeafCount = 
      match x with 
      | Leaf _ -> 1 
      | Node leaves -> leaves |> List.sumBy (fun x -> x.LeafCount) 
     member x.LeafCountAsString = x.ToString() 
     override x.ToString() = 
      // For test output, we don't care about leaf data, just count 
      match x with 
      | Leaf -> "Tree with a total of 1 leaf" 
      | Node -> sprintf "Tree with a total of %d leaves" x.LeafCount 

注:我还没有在F#中测试了这个,只是输进了堆栈溢出评论框 - 所以它可能是我搞砸ToString()部分。 (我不记得,也无法用Google快速找到,覆盖是否在with关键字之后或之前)。但我知道StructuredFormatDisplay属性就是你想要的,因为我自己使用这个来获得FsCheck的自定义输出。

顺便说一句,你也可以在我的例子中的复杂记录类型上设置StructuredFormatDisplay属性。举例来说,如果你有,你关心的树形结构,但不是叶子的内容,你会写一个测试,如:

// Example 3 
[<StructuredFormatDisplay("LeafRecord")>] // Note no {} and no property 
type ComplicatedRecord = { ... } 
type Tree = 
    | Leaf of ComplicatedRecord 
    | Node of Tree list 
    with 
     member x.LeafCount = 
      match x with 
      | Leaf _ -> 1 
      | Node leaves -> leaves |> List.sumBy (fun x -> x.LeafCount) 
     override x.ToString() = 
      // For test output, we don't care about leaf data, just count 
      match x with 
      | Leaf -> "Tree with a total of 1 leaf" 
      | Node -> sprintf "Tree with a total of %d leaves" x.LeafCount 

现在所有的ComplicatedRecord情况下,不管他们的内容,将在输出中显示为文本LeafRecord,您可以更好地专注于树形结构 - 并且不需要在Tree类型上设置StructuredFormatDisplay属性。

这不是一个完全理想的解决方案,因为您可能需要随时调整StructuredFormatDisplay属性,正如您正在运行的各种测试所需的那样。 (对于某些测试,您可能需要关注叶数据的一部分,对于其他人,您希望完全忽略叶数据,等等)。在您投入生产之前,您可能需要将该属性取出。但是,直到FsCheck获得“给我一个功能,用”config参数格式化失败的测试数据,这是让您的测试数据格式化为您需要的最佳方式。

+0

谢谢,现在我可以根据需要打印我的图。剩下一点。您提到无法更改为FsCheck的输出功能。是否真的没有办法以某种方式继承和重写功能(是的,这是邪恶的OO思想)?这是否意味着关于我的问题的第二个问题,我没有机会输出例如在我的例子中测试失败时生成的图形和它的克隆? –

+0

如果你看看[FsCheck源代码](https://github.com/fscheck/FsCheck/Blob/master/src/FsCheck/Runner.fs),你会发现'argumentsToString'函数是一个函数调用'sprintf“%A”',然后它又被'onFailureToString'调用。你实际上可以通过实现'IRunner'接口来实现你自己的测试运行者,所以我不完全正确这是唯一的方法。不过,这当然是最简单的方法。 – rmunn

+0

如果你想输出原始图和它的克隆,最简单的方法就是在你的测试中:'let result =(originalGraph = clonedGraph);如果没有结果,则printfn“原始图:%A”originalGraph; printfn“克隆图:%A”clonedGraph; result'。像这样简单。 – rmunn