FsCheck 设置和拆卸
FsCheck with Setup and Teardown
总结
在每个 属性 案例之前是否有任何事件可以 运行 以便我可以 运行 为 [=82= 的每个 运行 设置和拆卸]?
完整版
我希望能够使用属性测试配对行为,例如“我总是可以获取书面记录”或“readAllLines 的输出等于 writeAllLines 的输入”。我还希望 属性 不关心操作集是如何实现的(即是否需要清理任何资源)。
属性 中的每个 运行 应该
- 独立于其他运行
- 在单个 运行
中的操作调用之间保持状态
- 不知道操作如何保持状态
- 不是资源泄漏
我正在使用 FsCheck 和 Expecto。示例将在 Expecto 中,但问题并非特定于框架。
使用基于示例的测试编写此类设置和拆卸非常容易。它们采用可预测的参数集,因此我可以 运行 在事件前后添加的包装器中添加它们。
let testWithEnv setup cleanup name test =
let testWrap () =
let (api, env) = setup ()
test api
cleanup env
testCase name testWrap
属性 测试无法做到这一点。他们有未知数量的参数,其中大部分将填充随机数据。
我可以很容易地应用这组成对的行为,但是任何创建的资源(如流)都不会被处置。
let testPropertyWithEnv setup cleanup name test =
let testWrap () =
let (api, env) = setup () // this is actually run once, but immutable so the individual runs don't leak state
test api // have to return this to pass along unapplied parameters
testProperty name testWrap
我调查过
亚军赛事
查看 how to run FsCheck tests 最近的钩子似乎是
OnStartFixture
每个测试只有 运行 一次 class
OnArguments
在每次通过后都是 运行 并且可能会用于 运行 清理
基于模型的特征
还有一个实验性的 Model-based testing features 可以工作。但是,考虑到我只关心操作的外部一致性,这似乎真的很重。我不想访问支持状态。
放弃并内联
我总能写
testProperty "name" (fun arg1 arg2 ->
let (api,env) = setup ()
//test code here
cleanup env
)
但我想避免每个 属性 中的样板文件和支持状态的暴露。
IDisposables
一次性对象也没有解决缺少设置挂钩的问题。
更多动手实践运行循环
我研究了 运行 在我的包装器中进行 属性 测试的方法,但是最小的 运行ner Check.one
是针对单个 属性 , 属性.
的 运行 之间没有钩子
惰性包装器
使包装器变得懒惰也不起作用testProperty name lazy(testWithSetup)
FsCheck 中实际上没有任何可以帮助您的东西,即使有,我也不认为它会直接暴露在 Expecto 中。我也不认为在 FsCheck 方面添加是那么简单 - 如果您在 FsCheck 存储库中打开一个问题,很高兴进一步讨论。
无论如何,巧妙地使用部分应用程序并以一些轻微的样板为代价,实际上可以包装“可变参数”函数,我认为这基本上就是您要问的。
看哪,代码:
// these types are here to make the signatures look nicer
type Api = Api
type Env = Env
let testProperty k =
// call the property with "random" arguments
for i in 0..2 do
k i (char i) (string i)
let setup() =
printfn "setup ran"
(Api, Env)
let teardown Env =
printfn "teardown ran"
let test0 Api arg1 =
printfn "test0 %A" arg1
let test1 Api (arg1:int) (arg2:char) =
printfn "test1 %A %A" arg1 arg2
let test2 Api arg1 arg2 arg3 =
printfn "testFun %A %A %A" arg1 arg2 arg3
let testWithEnv (setup:unit -> Api*Env) (teardown: Env -> unit) (test: Api -> 'a) (k: 'a -> unit) :unit =
let (api, env) = setup()
k (test api)
teardown env
let (<*>) (f,k) arg =
f, (fun c -> k c arg)
let (<!>) f arg =
f, (fun k -> k arg)
let run (f, k) = f k
testProperty (fun arg1 arg2 arg3 ->
testWithEnv setup teardown test2 <!> arg1 <*> arg2 <*> arg3 |> run
)
这里的想法是,您使用运算符 <!>
和 <*>
将任意数量的任意类型的参数串在一起,将其传递给 testWithEnv
函数,然后调用 run
上的结果。运算符和 run
基本上是构建和应用可变参数列表所必需的。
它都是类型安全的,即如果你忘记传递一个参数或者它的类型错误,你会得到一个类型错误,虽然它可能不像正常的函数应用程序那样清晰。
我建议将其粘贴到 IDE 中并检查类型,这将极大地帮助理解正在发生的事情。
您可以使用其他一些样式来编写此内容。例如使用稍微不同的定义和 the
函数,您可以编写类似
的内容
let (<+>) k arg =
fun c -> k c arg
let the arg =
fun k -> k arg
testWithEnv setup teardown test2 (the arg1 <+> arg2 <+> arg3)
这用 the
替换了 run
函数并且只需要一个运算符 <+>
。可能还有其他方法可以削减它,选择你的毒药。
总结
在每个 属性 案例之前是否有任何事件可以 运行 以便我可以 运行 为 [=82= 的每个 运行 设置和拆卸]?
完整版
我希望能够使用属性测试配对行为,例如“我总是可以获取书面记录”或“readAllLines 的输出等于 writeAllLines 的输入”。我还希望 属性 不关心操作集是如何实现的(即是否需要清理任何资源)。
属性 中的每个 运行 应该
- 独立于其他运行
- 在单个 运行 中的操作调用之间保持状态
- 不知道操作如何保持状态
- 不是资源泄漏
我正在使用 FsCheck 和 Expecto。示例将在 Expecto 中,但问题并非特定于框架。
使用基于示例的测试编写此类设置和拆卸非常容易。它们采用可预测的参数集,因此我可以 运行 在事件前后添加的包装器中添加它们。
let testWithEnv setup cleanup name test =
let testWrap () =
let (api, env) = setup ()
test api
cleanup env
testCase name testWrap
属性 测试无法做到这一点。他们有未知数量的参数,其中大部分将填充随机数据。
我可以很容易地应用这组成对的行为,但是任何创建的资源(如流)都不会被处置。
let testPropertyWithEnv setup cleanup name test =
let testWrap () =
let (api, env) = setup () // this is actually run once, but immutable so the individual runs don't leak state
test api // have to return this to pass along unapplied parameters
testProperty name testWrap
我调查过
亚军赛事查看 how to run FsCheck tests 最近的钩子似乎是
OnStartFixture
每个测试只有 运行 一次 classOnArguments
在每次通过后都是 运行 并且可能会用于 运行 清理
还有一个实验性的 Model-based testing features 可以工作。但是,考虑到我只关心操作的外部一致性,这似乎真的很重。我不想访问支持状态。
放弃并内联我总能写
testProperty "name" (fun arg1 arg2 ->
let (api,env) = setup ()
//test code here
cleanup env
)
但我想避免每个 属性 中的样板文件和支持状态的暴露。
IDisposables一次性对象也没有解决缺少设置挂钩的问题。
更多动手实践运行循环我研究了 运行 在我的包装器中进行 属性 测试的方法,但是最小的 运行ner Check.one
是针对单个 属性 , 属性.
使包装器变得懒惰也不起作用testProperty name lazy(testWithSetup)
FsCheck 中实际上没有任何可以帮助您的东西,即使有,我也不认为它会直接暴露在 Expecto 中。我也不认为在 FsCheck 方面添加是那么简单 - 如果您在 FsCheck 存储库中打开一个问题,很高兴进一步讨论。
无论如何,巧妙地使用部分应用程序并以一些轻微的样板为代价,实际上可以包装“可变参数”函数,我认为这基本上就是您要问的。
看哪,代码:
// these types are here to make the signatures look nicer
type Api = Api
type Env = Env
let testProperty k =
// call the property with "random" arguments
for i in 0..2 do
k i (char i) (string i)
let setup() =
printfn "setup ran"
(Api, Env)
let teardown Env =
printfn "teardown ran"
let test0 Api arg1 =
printfn "test0 %A" arg1
let test1 Api (arg1:int) (arg2:char) =
printfn "test1 %A %A" arg1 arg2
let test2 Api arg1 arg2 arg3 =
printfn "testFun %A %A %A" arg1 arg2 arg3
let testWithEnv (setup:unit -> Api*Env) (teardown: Env -> unit) (test: Api -> 'a) (k: 'a -> unit) :unit =
let (api, env) = setup()
k (test api)
teardown env
let (<*>) (f,k) arg =
f, (fun c -> k c arg)
let (<!>) f arg =
f, (fun k -> k arg)
let run (f, k) = f k
testProperty (fun arg1 arg2 arg3 ->
testWithEnv setup teardown test2 <!> arg1 <*> arg2 <*> arg3 |> run
)
这里的想法是,您使用运算符 <!>
和 <*>
将任意数量的任意类型的参数串在一起,将其传递给 testWithEnv
函数,然后调用 run
上的结果。运算符和 run
基本上是构建和应用可变参数列表所必需的。
它都是类型安全的,即如果你忘记传递一个参数或者它的类型错误,你会得到一个类型错误,虽然它可能不像正常的函数应用程序那样清晰。
我建议将其粘贴到 IDE 中并检查类型,这将极大地帮助理解正在发生的事情。
您可以使用其他一些样式来编写此内容。例如使用稍微不同的定义和 the
函数,您可以编写类似
let (<+>) k arg =
fun c -> k c arg
let the arg =
fun k -> k arg
testWithEnv setup teardown test2 (the arg1 <+> arg2 <+> arg3)
这用 the
替换了 run
函数并且只需要一个运算符 <+>
。可能还有其他方法可以削减它,选择你的毒药。