代码之家  ›  专栏  ›  技术社区  ›  Tomas Aschan

有没有办法对副作用进行单元测试?

  •  11
  • Tomas Aschan  · 技术社区  · 14 年前

    任何代码都可以提供副作用。大多数时候,副作用可能是设计不好和/或需要重构的标志,但当进行单元测试时,我发现很难进行测试。考虑以下示例:

    [Test]
    public void TrimAll_Removes_All_Spaces()
    {
        // Arrange
        var testSubject = "A    string  with     lots   of     space";
        var expectedResult = "Astringwithlotsofspace";
    
        // Act
        var result = testSubject.TrimAll();
    
        // Assert
        Assert.AreEqual(expectedResult, result);
    }
    

    测试以下扩展:

    public static string TrimAll(this string str)
    {
        PokeAround();
    
        return str.Replace(" ", "");
    }
    

    测试会通过,但没有副作用。呼叫的影响 PokeAround 会被完全忽视。

    既然你不知道 可能是任何东西你如何编写一个测试来防范它?有可能吗?

    澄清: 关于这个问题有几点评论 猫鼬 因为完全未知是一个非常不可能的场景,因为我们在编写测试时有源代码。不过,我问这个问题的原因是想找到一种防止副作用的方法 后来 . 也就是说,当我编写测试时,我可能会让exension方法如下所示:

    public static string TrimAll(this string str)
    {
        return str.Replace(" ", "");
    }
    

    猫鼬 打电话。我希望我已经写的测试失败,因为他失败了。

    10 回复  |  直到 14 年前
        1
  •  13
  •   Péter Török    14 年前

    这就是所谓的 传感 Working Effectively With Legacy Code

    既然你不知道什么是猫头鹰-它可能是任何东西!

    因为我们讨论的是单元测试,所以这几乎不应该是真的—单元测试是真的 白盒 测试,并且代码应该在那里供您检查。除非它在某个封闭源代码的第三方库中,在这种情况下 不能根据定义对其进行单元测试(也许您需要功能/验收测试,但这是一个完全不同的问题……)。

    更新:

    1. 不能,
    2. 不应该。

    你不能这样做,因为在现实生活中(非平凡的)程序中,没有合理的方法来检测方法调用是否没有副作用。你要找的是一些检查 整个宇宙 除了这个和这个小东西没有改变。即使从一个卑微的程序来看,这个宇宙也是 巨大的 . 一个方法调用可以创建/更新/删除任意数量的本地对象(其中许多甚至在单元测试环境中看不到)、在可用的本地/网络文件系统上触摸文件、执行DB请求、进行远程过程调用。。。

    你不应该这样做,因为这是由你的同事在将来做出的改变来负责对他/她的改变进行单元测试。如果您不相信这会发生,那么您就遇到了人员或流程问题,而不是单元测试问题。

        2
  •  3
  •   mernst    8 年前

    1. 单元测试
    2. 代码检查/审查
    3. 断言
    4. 静态分析工具,如 FindBugs 以及 Checker Framework

    考试通过了,一切都好。一个月后,我休假的时候,一个同事打电话给我。我希望我已经写的测试失败,因为他失败了。

    是什么让你觉得你的同事也不会改变测试?

        4
  •  2
  •   S.Lott    14 年前

    这个问题似是而非。这种情况在现实世界中不大可能发生。

    1. 你总是知道什么是猫鼬。这是单元测试。你有消息来源。

      如果你不知道什么是PokeAround,你有人谁是特别邪恶和阻止成功。他们需要新工作。或者你会。

    2. 你必须为这只猫鼬使用仿制品,这样你才能观察到副作用。

    “防范以后添加的副作用。”

    这不是一段神秘代码的例子。你还知道什么是美洲狮。你总是知道什么是猫鼬。

    这就是为什么我们要做回归测试。 http://en.wikipedia.org/wiki/Regression_testing

    • 您仍然使用独立的单元测试来测试PokeAround。

        5
  •  1
  •   Andrew Grimm atk    14 年前

    我不使用这种语言编程,但以下是测试原始字符串是否被修改的一种方法:

    [Test]
    public void TrimAll_Removes_All_Spaces()
    {
        // Arrange
        var testSubject = "A    string  with     lots   of     space";
        var testSubjectCopy = "A    string  with     lots   of     space";
        var expectedResult = "Astringwithlotsofspace";
    
        // Act
        var result = testSubject.TrimAll();
    
        // Assert
        Assert.AreEqual(expectedResult, result);
    
        // Check for side effects
        Assert.AreEqual(testSubjectCopy, testSubject);
    }
    
        6
  •  0
  •   Justin Ethier    14 年前

    您可以从另一个角度来看待它,即通过为此代码编写单元测试,您强迫自己承认代码中的问题(副作用)。然后,您可以重新编写/重构代码,使其可测试,可能是通过移动 PokeAround

        7
  •  0
  •   OtherDevOpsGene    14 年前

    我不确定你能不能。毕竟,它是一个预期的效果还是一个副作用取决于设计师的意图。

    我建议你断言“副作用”代码是不变的。

    还有,像 Jester 也许会有帮助,但我不认为这对你的例子有什么影响。

        8
  •  0
  •   Grant Crofton    14 年前

    取决于你所说的副作用-我的意思是,也许PokeAround()做了一些重要的事情需要做。副作用如何分类?

    不管怎样,我所知道的并没有什么特别的技术/技巧可以在单个单元测试中防止副作用,但是只要您对所有代码都有测试覆盖率,任何不需要的副作用都有希望被至少一个测试发现。

    BDD/集成测试工具也有助于解决这个问题,因为它们(通常)测试更大的功能领域,而不仅仅是单个类/方法。

    Design By Contract (DBC)。这允许您指定前置和后置条件,以及不变量,因此,如果使用无效参数调用方法、返回无效值或对象进入无效状态,则会引发某种错误。

        9
  •  0
  •   hol    14 年前

    不可能。副作用的测试在任何测试阶段都是困难的。在不同的项目(产品开发、工具开发、业务应用开发、游戏开发等)中是不同的。

    没有一个完整的回归测试副作用是找不到的。

    在一个典型的项目(我经历过)中,“这个变更是否有任何副作用”这个问题经常在项目快结束时(即将上线)或者有人想在已经生产的系统中添加一个热修复程序时被问到。我发现在没有回归测试的情况下,惟一的(仍然有风险的)质量控制措施是代码评审。

        10
  •  0
  •   Josh Kelley    14 年前

    一种技术是随机排列单元测试的顺序。如果您的测试套件相当全面,那么随机化测试顺序可能会显示意外的副作用、错误地依赖于以前测试状态的测试,等等。

    Google Test (对于C++)CAN do this ; 我不知道其他框架有这个特性。