【发布时间】:2013-06-12 16:56:27
【问题描述】:
我喜欢编写纯函数的想法,但我无法理解将它们组合在一起以生成可测试代码的方法。我习惯于提取类然后适当地存根,并且觉得我缺少对函数式编程的一些关键见解。
这是我从当前面临的问题中精简的示例。
我想获取一份日期列表,并筛选出符合“机会”标准的日期。
在 C# 中它看起来像:
static List<List<DateTime>> Opportunities(List<List<DateTime>> dates)
{
return dates.Where(ds => HasOpportunity(ds)).ToList();
}
static bool HasOpportunity(List<DateTime> dates)
{
var threshold = 0.05D;
var current = OpportunityProbability(dates, DateTime.Now);
var previous = OpportunityProbability(dates, DateTime.Now.Subtract(TimeSpan.FromDays(30)));
return previous >= threshold && current < threshold;
}
static double OpportunityProbability(List<DateTime> dates, DateTime endDate)
{
// does lots of math
return 0.0D;
}
所以在提示我们有OpportunityProbability 我知道如何测试。我遇到的麻烦是在HasOpportunity 和更进一步的链条 (Opportunities)。
我知道如何测试HasOpportunity 的唯一方法是删除OpportunityProbability,但我做不到。而且我不想创建假数据来满足OpportunityProbability 的设计,以便测试HasOpportunity。所以尽管这两个函数都是纯函数,但它是不可测试的,我觉得它的设计很糟糕。
因此我觉得我在设计糟糕的函数式代码:)
我关心HasOpportunity 主要是布尔测试。给定两个双精度数和一个阈值,进行比较并返回结果。为了获得这两个双打,它使用了一个需要日期列表和日期列表的函数。这导致HasOpportunity 也负责确定日期(DateTime.Now 和 30 天前)。也许我可以把它分开:
static bool HasOpportunity(double probability1, double probability2)
{
var threshold = 0.05D;
return probability2 >= threshold && probability1 < threshold;
}
所以这显然是可测试的。我什至可以提高门槛:
static bool HasOpportunity(double threshold, double probability1, double probability2)
{
return probability2 >= threshold && probability1 < threshold;
}
所以这更通用。
我这样做时遇到的问题是我刚刚将内容移至Opportunities:
static List<List<DateTime>> Opportunities(List<List<DateTime>> dates)
{
return dates.Where(ds => {
var current = OpportunityProbability(ds, DateTime.Now);
var previous = OpportunityProbability(ds, DateTime.Now.Subtract(TimeSpan.FromDays(30)));
return HasOpportunity(0.05D, current, previous);
}).ToList();
}
这是我不知道下一步要采取的地方。
功能霸主有什么想法吗?帮我用 C# 写 F#,在此先感谢!
更新
所以再往前走一步,我可以得到:
static List<List<DateTime>> Opportunities(double threshold, DateTime currentDate, DateTime previousDate, List<List<DateTime>> dates)
{
return dates.Where(ds => {
var current = OpportunityProbability(ds, currentDate);
var previous = OpportunityProbability(ds, previousDate);
return HasOpportunity(threshold, current, previous);
}).ToList();
}
所以我仍然不知道如何测试这个,但很好的是这个函数的参数最终定义了机会是什么:
- 阈值
- 第一次约会
- 第二个日期
然后给出一个日期列表,它可以给你机会。
【问题讨论】:
-
想评论为什么投反对票?
-
“功能霸主有什么想法吗?” 有什么想法?如何测试内置运算符? “帮我用 C# 写 F#,先谢谢了!” 什么 F#?
-
我觉得这是非常通用的代码。它在 C# 中,因为我必须选择一个,但我可以很容易地用 F# 重写它。整个问题是基于如何以可测试的方式组合纯函数。我可以写得更抽象一些,但我认为它没有任何帮助。
-
我认为你的最后一步很好——因为它是一个纯函数,你可以使用 NUnit 中的随机测试功能,或者 FsCheck(也可以用于 C# 代码)。跨度>
-
如果回答能帮助您解决问题,请记得接受。
标签: c# f# functional-programming functional-testing c#-to-f#