【问题标题】:Catch exception for testing捕获异常进行测试
【发布时间】:2017-10-30 04:14:34
【问题描述】:

我目前正在学习 F# 课程。我正在为我重写的与作业相关的函数进行黑盒测试。在此测试期间,我应该通过输入错误的格式(将字符串提供给需要 int 的函数)来测试函数,我希望能够捕获异常并允许测试继续运行而不是退出。我怎样才能在 f# 中做到这一点?

这是我迄今为止尝试过的:

#r "msort.dll"

let tests = [
    ("Sorted lists", [
        ([1;2;3;4;5;6;7;8;9;10], [1;2;3;4;5;6;7;8;9;10]);
        ([5000;6000;7000;8000;9000;10000;11000;12000;13000;14000], [5000;6000;7000;8000;9000;10000;11000;12000;13000;14000]);
        ([10;9;8;7;6;5;4;3;2;1], [1;2;3;4;5;6;7;8;9;10]);
        ([14000;13000;12000;11000;10000;9000;8000;7000;6000;5000], [5000;6000;7000;8000;9000;10000;11000;12000;13000;14000]);
    ])
    ("Wrong input formats", [
        ([], []);
        (["hello"], []);
    ])
]

printfn "Black-box testing of Merge Sort"
for i = 0 to tests.Length-1 do
    let (testName, testSet) = tests.[i]
    printfn "%d. %s" (i+1) testName
    for j = 0 to testSet.Length - 1 do
        try
            let (input, expected) = testSet.[j]
            let result = (msort.sort input)
            printfn "test %d - %b" (j+1) (result = expected)
        with
            | _ as ex -> printfn "error occured"

我在想我可以用通配符捕获任何异常,并在打印字符串“发生错误”后继续测试,但程序只是以异常关闭:

error FS0001: This expression was expected to have type
    'int'    
but here has type
    'string'    

我正在测试的函数是一个重写的合并排序版本以使用模式匹配。这是函数的代码,编译成“msort.dll”

module msort

let rec merge (xs:int list) (ys:int list): int list =
  match xs with
    | []  -> ys                   // if xs is empty return ys
    | xs when ys = [] -> xs       // if ys is empty return xs
    | _   ->                      // if non of the above is true
      let x = List.head xs
      let y = List.head ys
      let xs = List.tail xs
      let ys = List.tail ys
      match x with
        | x when x <= y -> x :: merge xs (y::ys) // if x < y merge x
        | x when x > y -> y :: merge (x::xs) ys // else merge y
        | _ -> []                               // otherwise something is wrong, return empty array

let rec sort (xs:int list): int list =
  let sz = List.length xs
  if sz < 2 then xs           // if the length of sz is under 2 we cannot split and so we return xs
  else let n = sz / 2
       let ys = xs.[0..n-1]   // ys = the first half of the input array
       let zs = xs.[n..sz-1]  // zs = the second half of the input array
       in merge (sort ys) (sort zs) // call merge with each half

【问题讨论】:

  • 这不是异常,是编译器错误。
  • 这里提示一下:tests的类型是什么?
  • 你说的没错,但我换个说法。有没有办法编译这些错误?该错误应该发生,但我希望能够执行带有这些错误的程序。测试的类型是一个包含元组和子数组的数组。
  • 您试图对原始列表过于聪明。这是一个编译错误,所以很明显你不能用它编译。并在["hello"] 行出现编译错误。这意味着无论您在程序的其余部分做什么,此列表都不是有效的 F# 代码。您当然可以捕获无效输入,对其进行测试等。您甚至可以使用 xUnit 或 Expecto 等测试库测试是否在无效输入上抛出正确的异常。但首先,将你的清单分成好清单和坏清单。可能你会想要 FsCheck 之类的东西来进行基于属性的测试。
  • @s952163 感谢您的详尽回答。所以我需要能够提前区分有效输入和无效输入,并据此正确处理。而不是将有效输入与无效输入放在一起并尝试一次性解析所有内容。我理解正确吗?

标签: error-handling f#


【解决方案1】:

正如 cmets 中所述,没有理由编写测试来尝试使用无效类型的输入调用您的 sort 函数 - F# 编译器将静态检查此类情况并阻止这些情况,因此没有人可以调用如果函数需要一个整数列表,则该函数带有一个字符串列表。

只有当你有一个不是静态类型安全的函数时,你正在尝试做的事情才有意义。例如,如果您有 unsafeSort 在排序之前将所有输入转换为整数。您可以使用System.Convert 定义类似的函数,并通过:?&gt; 进行转换。

这是非常丑陋和不安全的,你永远不应该这样做,因为它违背了将 F# 与静态类型一起使用的目的,但你可以从技术上做到这一点:

let unsafeSort list = 
  msort.sort [ for v in list -> System.Convert.ChangeType(v, typeof<int>) :?> int ]
  |> List.map box

然后您可以将测试的数据表示为对象,这样您就可以混合字符串和整数:

let tests = [
    ("Sorted lists", [
        ([box 1;box 3;box 2], [box 1;box 2;box 3]);
    ])
    ("Wrong input formats", [
        (["hello"], []);
    ])
]

并且使用以下命令运行测试将报告一次通过和一次错误:

for testName, testSet in tests do
  for input, expected in testSet do
    try
        let result = unsafeSort input
        printfn "- %b" (result = expected)
    with
        | _ as ex -> printfn "error occured"

如前所述,这只是从技术方面回答您的问题 - 这样做是一个非常糟糕的主意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-10
    • 2020-06-30
    相关资源
    最近更新 更多