【问题标题】:When is it appropriate to bypass encapsulation in unit tests?什么时候适合在单元测试中绕过封装?
【发布时间】:2014-05-16 19:01:04
【问题描述】:

我最近开始了解powermock's Whitebox 功能。 (总而言之,它允许您直接测试私有方法或直接从单元测试中修改私有成员——同时保持私有!)

我知道有一些想法不赞成对可见方法以外的任何东西进行单元测试,但是该死的,有时您只需要一个简单的测试来确保深层辅助方法正在做它应该做的事情。 ..无需经历巨大的开销来准备父方法过滤到您的方法所需的参数和模拟..然后您必须做魔术来提取该内部方法的结果。简而言之,有时测试这些内部方法需要最复杂且最难遵循的单元测试。 (而且,仅供参考,我们公司有 100% 单元测试覆盖率的政策。必须测试这些方法!

我知道one methodology更改 方法访问器以允许进行单元测试(例如,从private 更改为protected,但powermock Whitebox 允许直接测试无需更改源代码。

不过,妈妈总是说,“仅仅因为你可以,并不意味着你应该。”

这样测试内部方法是否合适?如果是这样,我应该使用什么经验法则?

【问题讨论】:

    标签: unit-testing testing junit powermock white-box


    【解决方案1】:

    让我们首先确认this is a partially religious debate

    我认为测试private 方法是您应该经常避免的事情,但也有例外。

    为什么我不应该测试private 方法?

    因为您是在测试您的应用程序是否工作,而不一定是如何工作。 private 方法不是您的程序公开的 API 的一部分,它本质上是一个实现细节。

    反对通过封装来解决对您设置的限制并仍然对其进行测试的一个常见论点是,它可能会破坏您测试该特定实现的测试。

    在“正常”测试中,我们测试 public 方法,因为它们构成了我们的程序:如果我们知道所有公共 API 都可以工作,那么我们就知道程序可以工作。如果您的应用程序结构良好,您将拥有多个 API 层,它们会不断将测试细化为更小的单元,以便您清楚地了解可能出现问题的地方。

    关于private 方法要意识到的是,它们总是会在某个时候被public 方法调用,该方法将得到测试。 private 方法是被测单元的实现细节,因此不应单独测试。

    此辅助方法不受 API 约束,因此无法保证其效果会保持不变;在不调整整体功能的情况下更改实现细节现在会破坏您对该方法的测试。因此,即使您的程序仍然可以正常工作,但您现在的测试已损坏。这些测试是不稳定的:不改变你的功能的重构仍然可能导致你不得不删除单元测试,因为它们不再相关(这本身很危险,因为你必须非常确定测试实际上是 多余的)。

    我为什么要测试private 方法?

    只有少数东西是非黑即白的,这不是其中之一。我相信新代码不应该测试 private 方法,但这就是我要区分的地方:新代码

    如果您继承旧代码库,则架构可能不是很漂亮。您可能不得不跳过障碍,整体代码流可能会更好。

    所以你要做的第一件事就是编写一些单元测试,保证在你破坏功能时告诉你。到目前为止一切顺利,我们现在可以进入实际的重构过程。让我们从这个 500 行的private 方法开始吧?

    记住前面的评论,您现在必须查看代码流并查看 public 方法使用该 private 方法并在您进行更改时密切关注它们。

    为了易于使用,我会在这里编写测试,仅针对您的应用程序的其余部分测试该 private 方法的合同:只要您从该方法得到的结果没有不同,您知道它不会影响任何其他遗留方法。有了这些知识,您现在可以轻松地重构该方法中的所有内容。当某些东西确实破裂时;您可以立即使用您的辅助方法,而不必通过公共 API 方法。

    请记住,我只会对大型重构操作执行此操作:这些首先是临时测试,而“实现细节”实际上是 500 行代码:这是一个包含很多细节的细节本身.

    我应该如何测试private 方法?

    关于如何使这些可测试,您有几个选择:

    反射

    我相信这是 WhiteBox 使用的。但它引起了一些人的注意:没有静态类型(更改方法名意味着破坏测试),it's expensive 和使用反射的代码往往更难阅读。

    私有对象

    我还没有查看确切的实现,但我很确定这在幕后使用了反射。基本上它通过一个方便的接口提供反射方法。我没有看到它使用太多(部分是因为我会使用反射作为绝对的最后手段)但它就在那里。

    MSDN

    提取

    这将是我的第一个方法:这个辅助方法是否重要到足以让它独立存在?您可能会对此说“是”,因为显然它已经足够重要,以至于您需要对其进行显式测试。

    公开

    最明显的选择:直接改成public。这遵循与提取相同的想法:“它可以独立存在吗?”。这里的不同之处在于您仍然将其视为当前类的一部分,您只是提升了它的访问权限,而将其提取到不同的类也表明您谈论的不仅仅是一个辅助方法。

    内部

    此选项采用中间路线:如果我将其设为内部(C# 上下文)并使用[InternalsVisibleTo] 属性,您可以将其设为internal,同时为您的测试程序集提供测试它的可能性(并且仍然远离它)来自公共 API)。

    这带来了风险,人们只将其视为实现细节,并在破坏测试时更改实现(占特定实现的原因)。

    此外,这还增加了您的应用程序和测试之间的耦合。


    大家都认为这取决于您自己的喜好:有几个选项可以测试private 方法,并且双方都有一些论据。就我个人而言,我会远离测试这些实现细节的麻烦,而是看看我能做些什么来改进我的架构。问题很可能会以这种方式自行解决。

    【讨论】:

    • 很好的答案!这几天我一直在思考和质疑,但我认为你的结论和建议是非常不可避免的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 1970-01-01
    • 2011-09-13
    • 2017-05-03
    相关资源
    最近更新 更多