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

Tom*_*han 11 unit-testing side-effects

任何代码都可以提供副作用.大多数情况下,副作用可能是设计糟糕和/或需要重构的标志,但在进行单元测试时,我发现很难对其进行测试.请考虑以下示例:

[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);
}
Run Code Online (Sandbox Code Playgroud)

测试以下扩展:

public static string TrimAll(this string str)
{
    PokeAround();

    return str.Replace(" ", "");
}
Run Code Online (Sandbox Code Playgroud)

测试将通过,但没有后卫的副作用.呼叫的影响PokeAround将完全被忽视.

鉴于你不知道是什么PokeAround- 它可能是任何东西! - 你怎么写一个防范它的测试?它可能吗?

澄清: 关于PokeAround完全未知是一个非常不可能的场景,有一些评论,因为我们在编写测试时有源代码.我问这个问题的原因是,找到一种方法来防止后来添加的副作用.也就是说,当我编写测试时,我可能会将exension方法看起来像这样:

public static string TrimAll(this string str)
{
    return str.Replace(" ", "");
}
Run Code Online (Sandbox Code Playgroud)

测试通过,一切都很好.然后,一个月后,当我正在度假时,一位同事加了PokeAround电话.我希望我已经写过的测试失败,因为他做了.

Pét*_*rök 12

这就是所谓的检测有效合作与遗留代码.也就是说,感知调用测试方法的效果.

鉴于你不知道PokeAround是什么 - 它可能是任何东西!

由于我们正在谈论的单元测试,这应该几乎是真实的-单元测试是白盒测试测试和代码是(应该是)那里为您检查.除非它在某个闭源第三方库中,在这种情况下你不需要测试它不能按定义进行单元测试(也许你需要功能/验收测试,但这是一个完全不同的问题......) .

更新:所以你想确保未来对你的单元测试方法的改变永远不会有任何意想不到的副作用?我觉得你

  1. 不能,
  2. 不应该.

你不能,因为没有明智的方法来检测现实生活(非平凡)程序中方法调用缺乏副作用.你正在寻找的是一些检查,整个宇宙的状态除了这个和这个小东西之外没有改变.即使从一个不起眼的程序的角度来看,这个宇宙也是巨大的.方法调用可以创建/更新/删除任意数量的本地对象(其中许多甚至无法从单元测试环境中看到),触摸可用本地/网络文件系统上的文件,执行数据库请求,进行远程过程调用. ..

你不应该这样做,因为你的同事要做出未来的改变来照顾他/她改变的单位测试.如果您不相信这会发生,那么您会遇到人员或流程问题,而不是单元测试问题.

  • @PéterTörök,实际上你**需要测试第三方库.图书馆可能并且可能会在某些时候发生变化.通过编写确认您的工作原理的测试,您可以安全地升级库,并立即知道预期行为是否有任何变化,而不是在用户开始调用后进入生产阶段. (4认同)