我正在尝试按照行为驱动开发的方式编程,该方法规定在编写任何代码前必须先编写失败的单元测试。
我的问题如下:
- 如何在私有方法中使用BDD?
- 如何对私有方法进行单元测试?
是否有比以下两种方法更好的解决方案:
- 首先将私有方法公开,然后在编写使用这些私有方法的公共方法时再将它们设为私有;
或者 - 在C#中将所有私有方法设置为internal并使用InternalsVisibleTo属性。
我正在尝试按照行为驱动开发的方式编程,该方法规定在编写任何代码前必须先编写失败的单元测试。
我的问题如下:
是否有比以下两种方法更好的解决方案:
当你先写测试代码时,你是针对公共接口编写的。此时没有私有方法。
然后你编写代码来通过测试。如果任何代码被重构为私有方法,那并不重要--它仍然应该存在,因为它被公共接口使用。
如果代码不是先写测试,那么在.NET中,可以使用反射直接访问私有方法;虽然这是最后的手段。
私有方法是内部实现细节,不应直接测试,因为它们将通过测试公共接口间接地被测试。如果在完全测试公共接口时某个私有方法未被覆盖,则该私有方法不是必需的,应将其删除。
一般来说,将测试代码与私有实现细节绑定是一个不好的想法。这会将测试与那些私有细节耦合起来,减少您随意更改这些细节的自由度,即使它们不影响公共接口和行为。这增加了编写和维护单元测试所需的工作量,这是一件负面的事情。您应该尽可能地进行覆盖,同时只绑定到公共接口。
简短回答: 你不需要测试私有方法。
如果你已经进行了良好的编程,你的测试代码覆盖率应该隐式地测试了私有方法。
* 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
简短回答: 你不能测试一个私有方法。
详细回答: 你不能测试一个私有方法,但如果你想测试它的功能,可以考虑重构你的代码。有两种简单的方法:
第一种方法很简单,但往往会让你在编写更多测试时出现问题,而后者则促进了更好的代码和测试设计。
牵强的回答: 好吧,我撒谎了。你可以使用一些反射魔法来测试私有方法(一些TDD工具支持测试私有方法)。但根据我的经验,这会导致复杂的单元测试。复杂的单元测试会导致更糟糕的代码。更糟糕的代码会导致愤怒。愤怒会导致仇恨。仇恨会导致痛苦……
生产代码变得更糟糕的直接影响是被测试的类倾向于变得庞大,并处理许多事情(违反单一职责原则),难以维护。这违背了TDD的目的,即使生产代码可测试、可扩展,更重要的是:可重用。
如果你正在为一个已部署的类编写测试,可以调查调用私有方法的所有内容,并相应地编写测试。如果你有机会重写这个类,那么请通过拆分这个类来进行重构。如果你很幸运,就会得到一些可重用的代码。
我同意不测试私有方法的观点,应该针对公共API编写测试,但你没有列出另一个选项。
你可以将这些方法设置为受保护的,然后从被测试的类派生出来。例如,您可以在派生类上使用公共方法公开基本受保护的方法:
public class TestableClassToTest : ClassToTest
{
public new void MethodToTest()
{
base.MethodToTest();
}
}
Reflector objectReflection = new Reflector(new ObjectWithprivateMethods());
objectReflection.InvokeMethod(AccessModifier.NonPublic,,"Add",1,6));
我已经与它斗争了一个多月,但是找到了答案:
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.
你应该只测试类的外部 API,即公共方法。如果你的测试没有触发私有方法中的代码,那么你需要编写更多的测试或重构这个类。
测试 API 的整个目的,特别是将要分发给第三方的 API,就是你可以随意更改类的内部结构,只要不破坏其公共方法的外部契约。
正如你所指出的,这就是 BDD 在使用模拟类的“传统” TDD 方面发挥作用的地方,其中每个方法调用都必须提前设置好进行测试。我对这两者都不是专家,希望其他人能比我更好地回答这个问题。