如何在BDD/TDD中对私有方法进行单元测试?

17

我正在尝试按照行为驱动开发的方式编程,该方法规定在编写任何代码前必须先编写失败的单元测试。

我的问题如下:

  • 如何在私有方法中使用BDD?
  • 如何对私有方法进行单元测试?

是否有比以下两种方法更好的解决方案:

  • 首先将私有方法公开,然后在编写使用这些私有方法的公共方法时再将它们设为私有;
    或者
  • 在C#中将所有私有方法设置为internal并使用InternalsVisibleTo属性。


3
请参考以下两个链接,了解如何对私有方法进行单元测试:https://dev59.com/bHVD5IYBdhLWcg3wRpaX 和 https://dev59.com/9HVD5IYBdhLWcg3wDXF3。这些回答提供了一些有见地的建议。 - David Johnstone
请忽略问题#1中得票最高的答案,或者直接跳到David发布的第二个答案。为您的团队着想;仅在万不得已时使用反射(以及其他聪明的方法来获取设计师不希望暴露的内容)。更喜欢简单的方式。 - Gishu
12个回答

22

当你先写测试代码时,你是针对公共接口编写的。此时没有私有方法。

然后你编写代码来通过测试。如果任何代码被重构为私有方法,那并不重要--它仍然应该存在,因为它被公共接口使用。

如果代码不是先写测试,那么在.NET中,可以使用反射直接访问私有方法;虽然这是最后的手段。


10

私有方法是内部实现细节,不应直接测试,因为它们将通过测试公共接口间接地被测试。如果在完全测试公共接口时某个私有方法未被覆盖,则该私有方法不是必需的,应将其删除。

一般来说,将测试代码与私有实现细节绑定是一个不好的想法。这会将测试与那些私有细节耦合起来,减少您随意更改这些细节的自由度,即使它们不影响公共接口和行为。这增加了编写和维护单元测试所需的工作量,这是一件负面的事情。您应该尽可能地进行覆盖,同时只绑定到公共接口。


8

简短回答: 你不需要测试私有方法。

如果你已经进行了良好的编程,你的测试代码覆盖率应该隐式地测试了私有方法。


6
如果存在一个私有方法,那么它就是被公共方法使用的。因此,我会为公共方法编写测试。
我编写测试来测试类的公共部分。如果类设计良好,则默认情况下会测试私有部分。
如果私有方法没有从公共方法中调用,那么它为什么存在?
在您的情况下,我会按照以下方式操作:
* Write failing test for the public method
* Write public method that calls the private method that doesn't exist yet(test still fails as your class is incomplete
* Write the private method
* Test should now pass

6

简短回答: 你不能测试一个私有方法。

详细回答: 你不能测试一个私有方法,但如果你想测试它的功能,可以考虑重构你的代码。有两种简单的方法:

  • 测试访问私有方法的公共方法。
  • 将私有代码提取到自己的类中,即移动实现,使其变得适当公开。

第一种方法很简单,但往往会让你在编写更多测试时出现问题,而后者则促进了更好的代码和测试设计。

牵强的回答: 好吧,我撒谎了。你可以使用一些反射魔法来测试私有方法(一些TDD工具支持测试私有方法)。但根据我的经验,这会导致复杂的单元测试。复杂的单元测试会导致更糟糕的代码。更糟糕的代码会导致愤怒。愤怒会导致仇恨。仇恨会导致痛苦……

生产代码变得更糟糕的直接影响是被测试的类倾向于变得庞大,并处理许多事情(违反单一职责原则),难以维护。这违背了TDD的目的,即使生产代码可测试、可扩展,更重要的是:可重用。

如果你正在为一个已部署的类编写测试,可以调查调用私有方法的所有内容,并相应地编写测试。如果你有机会重写这个类,那么请通过拆分这个类来进行重构。如果你很幸运,就会得到一些可重用的代码。


长答案可能会略微修改以表明您可以使用TypeMock Isolator在.NET中测试私有方法- http://learn.typemock.com/isolator-features/test-the-un-testable/change-behavior-of-internalprivate-or-protected-methods-easi.html - Russ Cam
修改了答案,但我对如何处理私有方法有一个激进的观点... 当我进行TDD时,我最终只会得到私有方法,因为这是重构步骤而不是我最初编写的测试导致的。也就是说,私有方法是从公共方法中提取出来的代码。如果我发现这段功能变得“更多”,通常将该代码移动到另一个类中是有意义的。 - Spoike

3
我们可以使用PrivateType和PrivateObject分别对静态方法和实例私有方法进行单元测试。以下两篇文章详细解释了这些技术:
1. 在C#.NET中对私有静态方法进行单元测试 链接 2. 在C#.NET中对私有实例方法进行单元测试 链接

2

我同意不测试私有方法的观点,应该针对公共API编写测试,但你没有列出另一个选项。

你可以将这些方法设置为受保护的,然后从被测试的类派生出来。例如,您可以在派生类上使用公共方法公开基本受保护的方法:

public class TestableClassToTest : ClassToTest
{
    public new void MethodToTest() 
    { 
        base.MethodToTest(); 
    } 
}

你可能已经在使用“提取和重写”模式来覆盖基类的虚拟属性进行依赖注入,如果是这样,那么这可能是一个可行的选项。

2
Mbunit Reflector可以帮助您完成这项任务。
Reflector objectReflection = new Reflector(new ObjectWithprivateMethods());
objectReflection.InvokeMethod(AccessModifier.NonPublic,,"Add",1,6));

这是一篇关于此主题的博客文章。


2

我已经与它斗争了一个多月,但是找到了答案:

        var objectOfPrivateMethod = new ObjectOfPrivateMethod(); //yes here is contructor
        object[] arguments = {  }; // here as Object you provide arguments

        var extractedPrivateMethod = typeof(ObjectOfPrivateMethod).GetMethod("Name_Of_Private_Method", BindingFlags.NonPublic|BindingFlags.Static); //if fails returns null. delete flag static if it's not static. Returns your method as an object.
        Assert.AreNotEqual(null, extractedPrivateMethod, "Mathod does not exist"); // good to catch if even exists.

        object result = extractedPrivateMethod.Invoke(null, arguments); // here as object you'll get return value of your function. change null for object of class where is method, if your method is not static 

that's all.


1

你应该只测试类的外部 API,即公共方法。如果你的测试没有触发私有方法中的代码,那么你需要编写更多的测试或重构这个类。

测试 API 的整个目的,特别是将要分发给第三方的 API,就是你可以随意更改类的内部结构,只要不破坏其公共方法的外部契约。

正如你所指出的,这就是 BDD 在使用模拟类的“传统” TDD 方面发挥作用的地方,其中每个方法调用都必须提前设置好进行测试。我对这两者都不是专家,希望其他人能比我更好地回答这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接