首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有安装和调试的FsCheck

带有安装和调试的FsCheck
EN

Stack Overflow用户
提问于 2020-12-22 00:20:29
回答 1查看 136关注 0票数 3

摘要

是否有任何事件可以在每个属性用例之前运行,以便我可以对属性的每次运行运行安装程序和解压缩?

完整版本

我希望能够用属性测试成对的行为,如“我总是可以获取书面记录”或“readAllLines的输出等于输入到writeAllLines”。我还希望属性不关心操作集是如何实现的(也就是说,如果需要清理任何资源)。

属性的每次运行都应该

  • 独立于其他运行
  • 在此单次运行中保持操作调用之间的状态
  • 不知道操作是如何维护状态的
  • 不是资源泄漏

我用的是FsCheck和Expecto。示例将出现在Expecto中,但问题并不是特定于框架的。

用基于示例的测试编写这种设置和拆卸是非常容易的。它们采用可预测的参数集,因此我可以在事件前后添加的包装器中运行它们。

代码语言:javascript
复制
let testWithEnv setup cleanup name test = 
    let testWrap () = 
        let (api, env) = setup ()
        test api
        cleanup env 
    testCase name testWrap

在属性测试中也不能这样做。他们有很多未知的论点,大部分都是随机数据。

我可以很容易地应用成对的行为集,但是任何创建的资源(如流)都不会被处理。

代码语言:javascript
复制
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

我查过了

跑步运动员项目

如何运行FsCheck测试,最近的钩子似乎是

  • OnStartFixture,每个测试类只运行一次。
  • 每次传递之后都会运行OnArguments,并且可能会运行清理工作。

基于模型的特征

也有实验的基于模型的测试特性可以工作。然而,考虑到我只关心操作的外部一致性,这似乎非常沉重。我不想进入支持状态。

放弃和衬里

我总能写

代码语言:javascript
复制
testProperty "name" (fun arg1 arg2 ->
    let (api,env) = setup ()
    //test code here
    cleanup env
)

但我想避免每一处房产的样板和支持状态的暴露。

IDisposables

一次性对象也不能解决缺少安装钩子的问题。

更多的手动运行循环

我在包装器中研究了运行属性测试的方法,但是最小的运行程序Check.one是针对单个属性的,在该属性的运行之间没有挂钩。

懒惰包装器

使包装器变懒也不适用于testProperty name lazy(testWithSetup)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-24 22:39:00

FsCheck中没有任何东西可以帮助您,即使有,我也不认为它会直接暴露在Expecto中。我也不认为在FsCheck方面添加是那么简单--如果您在FsCheck回购中打开一个问题,那么很乐意进一步讨论这个问题。

在任何情况下,只要巧妙地使用部分应用程序,并以一些轻微的样板为代价,实际上就有可能包装"variadic“函数,我认为这基本上就是您所要求的。

看,守则:

代码语言:javascript
复制
// 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函数,您可以编写以下内容

代码语言:javascript
复制
let (<+>) k arg  =
    fun c -> k c arg

let the arg =
    fun k -> k arg

testWithEnv setup teardown test2 (the arg1 <+> arg2 <+> arg3)

这将run函数替换为the,并且只需要一个操作符<+>。也许还有其他的方法来减少它,挑选你的毒药。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65401727

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档