在F#

时间:2017-03-19 21:10:19

标签: unit-testing f# functional-programming higher-order-functions

采用以下F#示例:

let parse mapDate mapLevel mapMessge (groups : string list) = 
    {
        DateTime = 
            mapDate(
                groups.[2] |> Int32.Parse, 
                groups.[0] |> Int32.Parse, 
                groups.[1] |> Int32.Parse)
        Level = mapLevel groups.[3]
        Message = mapMessge  groups.[4]
    }

我可以独立单独测试地图函数,但是我如何单元测试这个函数正确调用作为参数传入的函数?

在C#中,我会使用模拟并验证对它们的调用。我最近观看了一个复数视频,讨论了函数式语言如何使用存根而不是模拟。在这里我可以传递一个函数,如果它没有得到预期的参数,但是我没有真正卖掉这种方法。

我只是想知道函数式编程中是否有任何模式用于单元测试这样的高阶函数?

2 个答案:

答案 0 :(得分:1)

好吧,让我不同意给出的答案。实际上,有一种很好的方法来测试更高阶的函数,甚至没有打扰他们可能采取的具体类型(我认为典型的HOF是完全通用的,但是没有区别:我建议的方法可以正确使用更严格的HFO)。 / p>

让我们采取一些非常简单的事情,这是每个人都熟悉的事情。 ['t] -> ['t]功能怎么样?它只需一个参数 - 无论类型的列表,并返回相同类型的列表。传统的OOP方法在这里不起作用:需要对't施加限制并测试该类型的某些特定参数;使作者对其实施更有信心的唯一方法是增加单元测试数量。

有很多名为"类别理论"在数学中。它是比较新的数学领域,从外部而不是从内部研究事物。为了能够从外部描述事物"你需要拿一件你感兴趣的东西,强迫它与你已经深入了解的东西互动。因此,类别理论教导用其他事物来描述的相互关系。我们不能在这里做同样的事情吗?..

确实,我们可以。这实际上非常简单:我们已经有了f : ['t] -> ['t],但还有其他任何东西可以让我们互动并定义一些常见 - 这些东西适用于每一个不管其他因素如何相互作用?我们采取任何 g: 't -> 'y。现在我们可以陈述:g (List.head (f ...) = List.head (List.map g (f ...))。我假设['t]类型的某个参数替换...。请注意:给定属性是通用的:它适用于指定签名的任何纯函数组合,无论其实现如何。另请注意通用性如何:它只有两个不同的&#34;对象&#34;通过&#34; composition&#34;互相交流,也可以用标准的F#(|>), (<|)运算符重写。

现在事实是对于任何更高阶(纯)函数存在这种通用属性;大多数情况下,有几十种。因此,能够在组成方面(对FP来说是常规的)指定它们的属性,保持在通用级别。在显式形式中具有这样的属性提供了一个机会来自动生成数百个测试,这些测试基于不仅通过它们的值(通常由单元测试完成,除了它们很少自动生成的事实)而且还根据类型而不同的输入。

答案 1 :(得分:-1)

纯函数更容易,因为您只需要测试parse函数的输出。您不应该像在命令式编程中那样使用副作用进行测试。

在编写大部分单元测试时,通常会使用最简单的函数参数,例如identity或类似函数。然后,您将编写一个名为“mapLevel应用于第四组”的测试,而您可以将mapLevel设置为易于识别为更改的内容,例如toUpper。这可以确保您不会意外地将mapLevel复制/粘贴到多个输出。然后对mapMessge进行类似的测试。