自定义 FsCheck 输出
Customise FsCheck output
我正在 VisualStudio 中使用 FsCheck 和 NUnit 进行测试。
目前的问题是:我设法生成了随机图(用于测试某些图功能)但是当测试失败时,FsCheck 吐出整个图并且它不使用 ToString 所以它直接转储原始记录列表和你什么也看不到。
此外,我不仅需要用于检查的输入图,还需要我在 运行 a 属性.
时创建的一些其他数据
那么我该如何更改 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
FsCheck 使用 sprintf "%A"
将测试参数转换为测试输出中的字符串,因此您需要做的是控制 %A
格式化程序如何格式化您的类型。根据How do I customize output of a custom type using printf?, the way to do that is with the 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# 中对此进行测试,只是将其输入到 Stack Overflow 评论框中——所以我可能搞砸了 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 获取 "Give me a function to format failed test data with" 配置参数之前,这是按照您需要的方式格式化测试数据的最佳方式。
您还可以使用标签在测试失败时显示您想要的任何内容:https://fscheck.github.io/FsCheck/Properties.html#And-Or-and-Labels
我正在 VisualStudio 中使用 FsCheck 和 NUnit 进行测试。
目前的问题是:我设法生成了随机图(用于测试某些图功能)但是当测试失败时,FsCheck 吐出整个图并且它不使用 ToString 所以它直接转储原始记录列表和你什么也看不到。
此外,我不仅需要用于检查的输入图,还需要我在 运行 a 属性.
时创建的一些其他数据那么我该如何更改 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
FsCheck 使用 sprintf "%A"
将测试参数转换为测试输出中的字符串,因此您需要做的是控制 %A
格式化程序如何格式化您的类型。根据How do I customize output of a custom type using printf?, the way to do that is with the 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# 中对此进行测试,只是将其输入到 Stack Overflow 评论框中——所以我可能搞砸了 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 获取 "Give me a function to format failed test data with" 配置参数之前,这是按照您需要的方式格式化测试数据的最佳方式。
您还可以使用标签在测试失败时显示您想要的任何内容:https://fscheck.github.io/FsCheck/Properties.html#And-Or-and-Labels