Unit testing 单元测试循环复杂,但在其他方面计算繁琐
假设我有一个计算器类,其主要功能是执行以下操作(此代码经过简化,以便于讨论,请不要对其样式发表评论) 至少有7条线路通过I类可能需要测试,包括trackCostMultiplier和hardstandingsRequireRemediation(6种组合)的组合以及异常条件。如果我有兴趣的话,我可能还想为除以零和溢出之类的东西添加一些 到目前为止还不错,我可以轻松、时尚地测试这些组合。实际上,我相信乘法和加法不太可能出错,所以只需对trackCostMultipler进行3次测试,对hardstandingsRequireRemediation进行2次测试,而不是测试所有可能的组合 然而,这是一个简单的例子,不幸的是,我们应用程序中的逻辑比这复杂得多,因此测试的数量可能会增加很多 有一些方法可以解决这种复杂性Unit testing 单元测试循环复杂,但在其他方面计算繁琐,unit-testing,test-double,Unit Testing,Test Double,假设我有一个计算器类,其主要功能是执行以下操作(此代码经过简化,以便于讨论,请不要对其样式发表评论) 至少有7条线路通过I类可能需要测试,包括trackCostMultiplier和hardstandingsRequireRemediation(6种组合)的组合以及异常条件。如果我有兴趣的话,我可能还想为除以零和溢出之类的东西添加一些 到目前为止还不错,我可以轻松、时尚地测试这些组合。实际上,我相信乘法和加法不太可能出错,所以只需对trackCostMultipler进行3次测试,对hardst
Cedd继续从评论到OP的讨论,如果您有,您可以先单独测试每个小部件,然后组合它们,并测试组合是否正确 由于组成函数是引用透明的,因此它们在逻辑上可以与其返回值互换。现在剩下的唯一一步就是证明整个函数正确地组合了各个函数 这件衣服非常适合你 例如,假设复杂计算有两部分:
module MyCalculations =
let complexPart1 x y = x + y // Imagine it's more complex
let complexPart2 x y = x - y // Imagine it's more complex
这两个函数都是确定性的,因此假设您确实想要测试组成这两个函数的facade
函数,您可以定义以下属性:
open FsCheck.Xunit
open Swensen.Unquote
open MyCalculations
[<Property>]
let facadeReturnsCorrectResult (x : int) (y : int) =
let actual = facade x y
let expected = (x, y) ||> complexPart1 |> complexPart2 x
expected =! actual
您需要另一个抽象级别来简化方法,以便更容易测试它们:
doStuff(trackConstructionType, referenceTrackWidth){
...
trackCostMultipler = countTrackCostMultipler(trackConstructionType)
countPilingCostPerArea = countPilingCostPerArea(referenceTrackWidth, trackCostMultipler)
...
}
countTrackCostMultipler(trackConstructionType){
double trackCostMultipler;
if (trackConstructionType = TrackConstructionType.Easy) trackCostMultipler = 0.8
else if (trackConstructionType = TrackConstructionType.Normal) trackCostMultipler = 1
else if (trackConstructionType = TrackConstructionType.Hard) trackCostMultipler = 1.3
else throw new OutOfRangeException("Unknown TrackConstructionType: " + trackConstructionType.ToString());
return trackCostMultipler;
}
countPilingCostPerArea(referenceTrackWidth, trackCostMultipler){
return TrackCostPerMeter / referenceTrackWidth * trackCostMultipler;
}
抱歉的代码,我不知道的语言,没有真正的问题
如果不想公开这些方法,那么必须将它们移动到单独的类中,并在那里公开它们。类名可以是TrackCostMultiplerAlgorithm或..Logic或..Counter,或类似的东西。因此,如果有更多不同的算法,您将能够将算法注入到更高抽象级别的代码中。一切都取决于实际的代码
哦,不要担心方法和类的长度,如果你真的需要一个新的方法或类,因为代码太复杂了,那么就创建一个吧!时间短并不重要。它也总是很容易理解,因为您可以在方法名中写入它的功能。该方法中的代码块只告诉我们它是如何使用函数式编程语言并用较小的函数组成大计算的?这听起来很有趣,你能给我举个例子吗?@cedd你需要@-回复Mark,让他看到你的评论(除非他有一个机器人或为他回答或评论过的每一个问题打开一个标签:D)。我推荐Mark的pluralsight课程集,但也推荐他的博客-这是必读的,但如果你想筛选,那里有很多这样的例子。谢谢Ruben。嗨@MarkSeemann,我试过阅读你的一些博客文章,但我看不到任何关于如何做的例子,我对函数式编程还不熟悉,你能告诉我吗请给我看一篇相关的博客文章/example/git hub/随便什么?谢谢。我对此做了更多的思考,并写了这篇博客文章,其中描述了一些我发现在类似这样的情况下有用的技术/重构。谢谢@MarkSeemann。我曾经在你的博客上看过一次钻石片,使用propert确实看起来更容易/更好基于y的测试。无论如何,回到上面的代码。我很高兴在C#中进行这样的测试。它包括测试complexPart1(&2)在测试进行组合的类时,在断言阶段单独使用它们。C#的缺点是必须以某种方式公开这些方法。函数式编程中是否存在一些我不知道的基本范式差异?另一个复杂之处是,该示例返回一个简单类型和onl一旦你开始编写许多返回复杂类型的函数,并且使用了大量的参数,我觉得测试就会变得非常模糊
let facade x y =
let intermediateResult = complexPart1 x y
complexPart2 x intermediateResult
doStuff(trackConstructionType, referenceTrackWidth){
...
trackCostMultipler = countTrackCostMultipler(trackConstructionType)
countPilingCostPerArea = countPilingCostPerArea(referenceTrackWidth, trackCostMultipler)
...
}
countTrackCostMultipler(trackConstructionType){
double trackCostMultipler;
if (trackConstructionType = TrackConstructionType.Easy) trackCostMultipler = 0.8
else if (trackConstructionType = TrackConstructionType.Normal) trackCostMultipler = 1
else if (trackConstructionType = TrackConstructionType.Hard) trackCostMultipler = 1.3
else throw new OutOfRangeException("Unknown TrackConstructionType: " + trackConstructionType.ToString());
return trackCostMultipler;
}
countPilingCostPerArea(referenceTrackWidth, trackCostMultipler){
return TrackCostPerMeter / referenceTrackWidth * trackCostMultipler;
}