F#

时间:2016-03-01 21:08:52

标签: f# testcase nunit-3.0

我正在尝试使用NUnit为F#项目设置测试套件。 看起来特别是在测试解析器和类型检查器之类的东西时,通常会有一个有效输入数据列表和一个无效数据列表。 测试本身实际上是相同的,所以我正在寻找一种聪明的方法来避免为每个数据项编写测试函数,而是从数据中分离测试函数。 显然,似乎有一些称为测试用例的东西,但我很难找到一般用于 NUnit 3和F#的综合文档,特别是最佳实践示例我的情景

任何指针和提示都非常适用!

2 个答案:

答案 0 :(得分:2)

这是NUnit 3.x的更新答案,因为我的原始答案显示了NUnit 2.x示例。

以下示例并不是全面的,但足以让您超越门槛并开始运行。值得注意的是,测试函数是使用参数列表而不是currying编写的。还有几种使用NUnit 3.x属性生成测试数据的方法,例如: Pairwise,但遗憾的是,没有任何可用属性知道如何为受歧视的联盟生成测试数据。

也不需要FSUnit而且我没有尝试使其工作,因为NUnint 2.x和3.x之间的区别非常大,以至于我很高兴只是为了让以下示例正常工作。也许将来我会更新这个答案。

namespace NUnit3Demo

open NUnit.Framework

module MyTest = 
    // ----------------------------------------------------------------------

    [<Pairwise>]
    let pairWiseTest([<Values("a", "b", "c")>] (a : string), [<Values("+", "-")>] (b :string), [<Values("x", "y")>] (c : string))
        = printfn "%A %A %A" a b c

    // ----------------------------------------------------------------------

    let divideCases1 =
        [
            12, 3, 4
            12, 2, 6
            12, 4, 3
        ] |> List.map (fun (q, n, d) -> TestCaseData(q,n,d))

    [<TestCaseSource("divideCases1")>]
    let caseSourceTest1(q:int, n:int, d:int) =
        Assert.AreEqual( q, n / d )

    // ----------------------------------------------------------------------

    let divideCases2 =
        seq {
            yield (12, 3, 4)
            yield (12, 2, 6)
            yield (12, 4, 3)
        }

    [<TestCaseSource("divideCases2")>]
    let caseSourceTest2(q:int, n:int, d:int) =
        Assert.AreEqual( q, n / d )

    // ----------------------------------------------------------------------

    [<TestCase(12,3,4)>]
    [<TestCase(12,2,6)>]
    [<TestCase(12,4,3)>]
    let testCaseTest(q:int, n:int, d:int) =
        Assert.AreEqual( q, n / d )

    // ----------------------------------------------------------------------

    let evenNumbers : int [] = [| 2; 4; 6; 8 |]

    [<TestCaseSource("evenNumbers")>]
    let caseSourceTest3 (num : int) =
        Assert.IsTrue(num % 2 = 0)

离开原始答案,因为OP在其他答案中已注明。

以下示例是3年前使用NUnit 2.x编写的,所以它有点过时,但应该给你一个理想的。

您创建测试数据的数组,然后索引到数组中以提取测试值和预期结果。关于这一点的好处是你不会为函数编写大量的单独测试。

这来自多年前我们中的一些人project

open NUnit.Framework
open FsUnit

let private filterValues : (int list * int list)[] = [| 
    (
        // idx 0
        // lib.filter.001
        [],
        []
    ); 
    (
        // idx 1
        // lib.filter.002
        [-2],
        [-2]
    );
    (
        // idx 2
        // lib.filter.003
        [-1],
        []
    );
    (
        // idx 3
        // lib.filter.004
        [0],
        [0]
    );
    (
        // idx 4
        // lib.filter.005
        [1],
        []
    );
    (
        // idx 5
        // lib.filter.006
        [1; 2],
        [2]
    );
    (
        // idx 6
        // lib.filter.007
        [1; 3],
        []
    );
    (
        // idx 7
        // lib.filter.008
        [2; 3],
        [2]
    );
    (
        // idx 8
        // lib.filter.009
        [1; 2; 3],
        [2]
    );
    (
        // idx 9
        // lib.filter.010
        [2; 3; 4],
        [2; 4]
    );
    |]

[<Test>]
[<TestCase(0, TestName = "lib.filter.01")>]
[<TestCase(1, TestName = "lib.filter.02")>]
[<TestCase(2, TestName = "lib.filter.03")>]
[<TestCase(3, TestName = "lib.filter.04")>]
[<TestCase(4, TestName = "lib.filter.05")>]
[<TestCase(5, TestName = "lib.filter.06")>]
[<TestCase(6, TestName = "lib.filter.07")>]
[<TestCase(7, TestName = "lib.filter.08")>]
[<TestCase(8, TestName = "lib.filter.09")>]
[<TestCase(9, TestName = "lib.filter.10")>]
let ``List filter`` idx = 
    let (list, _) = filterValues.[idx]
    let (_, result) = filterValues.[idx]
    List.filter (fun x -> x % 2 = 0) list 
    |> should equal result
    filter (fun x -> x % 2 = 0) list 
    |> should equal result
IIRC使用NUnit和F#的问题是要记住在正确的位置使用<>

答案 1 :(得分:0)

到那天结束时,我意识到我不应该首先使用阵列!

我终于明白了TestCase机制应该如何工作: 它只是将注释的内容作为参数传递给现在不再是unit->unit的函数,而是(在我的情况下)string->unit。因此,我的所有数据项现在都粘贴到单独的TestCase注释中,并且数组已经消失。当然,这可能看起来有点奇怪,让TestCase注释的内容跨越多行代码,但数组也不漂亮,所以就这样吧。

不幸的是,我的解决方案并非普遍适用,例如:对于Guy Coder上面的代码不起作用。原因如下:https://stackoverflow.com/a/28015585/2289899他们说:

  

CLI对属性参数的种类有限制:

     
      
  • 原语:bool,int,float等
  •   
  • 枚举
  •   
  • 字符串
  •   
  • 类型引用:System.Type
  •   
  • 'kinda objects':盒装(如果需要)上面类型的表示
  •   
  • 来自上面的一种类型的一维数组(即,不允许嵌套数组)
  •   
     

所以我们可以在这一点上得出结论,你不能使用元组作为   属性参数的类型。