Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/github/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 单元测试循环复杂,但在其他方面计算繁琐_Unit Testing_Test Double - Fatal编程技术网

Unit testing 单元测试循环复杂,但在其他方面计算繁琐

Unit testing 单元测试循环复杂,但在其他方面计算繁琐,unit-testing,test-double,Unit Testing,Test Double,假设我有一个计算器类,其主要功能是执行以下操作(此代码经过简化,以便于讨论,请不要对其样式发表评论) 至少有7条线路通过I类可能需要测试,包括trackCostMultiplier和hardstandingsRequireRemediation(6种组合)的组合以及异常条件。如果我有兴趣的话,我可能还想为除以零和溢出之类的东西添加一些 到目前为止还不错,我可以轻松、时尚地测试这些组合。实际上,我相信乘法和加法不太可能出错,所以只需对trackCostMultipler进行3次测试,对hardst

假设我有一个计算器类,其主要功能是执行以下操作(此代码经过简化,以便于讨论,请不要对其样式发表评论)

至少有7条线路通过I类可能需要测试,包括trackCostMultiplier和hardstandingsRequireRemediation(6种组合)的组合以及异常条件。如果我有兴趣的话,我可能还想为除以零和溢出之类的东西添加一些

到目前为止还不错,我可以轻松、时尚地测试这些组合。实际上,我相信乘法和加法不太可能出错,所以只需对trackCostMultipler进行3次测试,对hardstandingsRequireRemediation进行2次测试,而不是测试所有可能的组合

然而,这是一个简单的例子,不幸的是,我们应用程序中的逻辑比这复杂得多,因此测试的数量可能会增加很多

有一些方法可以解决这种复杂性

  • 将trackCostMultipler计算提取到同一类中的方法
  • 这是一件好事,但除非我公开这个方法,否则它不会帮助我测试它,这是“生产中的测试逻辑”的一种形式。我经常以实用主义的名义这样做,但如果可以的话,我想避免这样做

  • 将trackCostMultipler计算推迟到其他类
  • 如果计算足够复杂,这似乎是一件好事,我可以轻松地测试这个新类。然而,我刚刚使原始类的测试变得更加复杂,因为我现在想传入某种类型的ITrackCostMultipler“Test Double”,检查它是否使用正确的参数调用,并检查其返回值是否正确使用。例如,当一个类有十个子计算器时,它的单元/集成测试变得非常大,并且很难理解

    我同时使用(1)和(2),它们给了我信心,使调试更快。然而,肯定有缺点,例如生产中的测试逻辑和模糊的测试

    我想知道测试循环复杂代码的其他经验是什么?有没有一种没有缺点的方法?我意识到特定于测试的子类可以绕过(1),但这对我来说似乎是一种遗留技术。也可以对输入进行操作,使计算的各个部分返回0(用于加法或减法)或1(用于乘法或除法),以使测试更容易,但这仅使我了解到这一点

    谢谢


    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;
    }