Answers:
通常,单元测试针对的是类的公共接口,其理论是实现是无关紧要的,只要从客户的角度来看结果是正确的即可。
因此,NUnit不提供任何测试非公开成员的机制。
System.Reflection
允许您使用绑定标志访问和调用非公共方法,因此您可以破解NUnit或设置自己的框架。或者(我想更容易),您可以设置一个编译时标志(#if TESTING)来更改访问修饰符,从而允许您使用现有框架。
虽然我同意单元测试的重点应该是公共接口,但是如果您也测试私有方法,您会对代码的印象更深。MS测试框架允许通过使用PrivateObject和PrivateType来实现这一点,而NUnit则不允许。我要做的是:
private MethodInfo GetMethod(string methodName)
{
if (string.IsNullOrWhiteSpace(methodName))
Assert.Fail("methodName cannot be null or whitespace");
var method = this.objectUnderTest.GetType()
.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
if (method == null)
Assert.Fail(string.Format("{0} method not found", methodName));
return method;
}
这种方式意味着您不必为了可测试性而牺牲封装性。请记住,如果要测试私有静态方法,则需要修改BindingFlags。上面的示例仅用于实例方法。
编写单元测试的常见模式是仅测试公共方法。
如果发现有许多要测试的私有方法,通常这表明您应该重构代码。
将这些方法在它们当前所在的类上公开是错误的。那会破坏您希望该类拥有的合同。
将他们转移到帮助者级别并在其中公开是正确的。您的API可能未公开此类。
这样,测试代码就不会与您的公共代码混在一起。
一个类似的问题是测试私有类,即。您不会从程序集中导出的类。在这种情况下,您可以使用属性InternalsVisibleTo将测试代码程序集明确地与生产代码程序集成为朋友。
通过将测试程序集声明为要测试的目标程序集的朋友程序集,可以测试私有方法。有关详细信息,请参见下面的链接:
http://msdn.microsoft.com/zh-CN/library/0tke9fxk.aspx
这很有用,因为它主要将测试代码与生产代码分开。我从未亲自使用过这种方法,因为我从未发现过需要这种方法。我想您可以使用它来测试极端的测试用例,而这些测试用例根本无法在测试环境中复制以查看您的代码如何处理它。
如前所述,您确实不需要测试私有方法。您比likley更希望将代码重构为较小的构建块。当您进行重构时,可能会帮助您的一个技巧是尝试考虑系统所关联的域,并考虑驻留在该域中的“实际”对象。系统中的对象/类应直接与真实对象相关,这将使您能够隔离出对象应包含的确切行为,并限制对象的责任。这将意味着您在逻辑上进行重构,而不仅仅是使测试特定方法成为可能。您将能够测试对象的行为。
如果您仍然需要进行内部测试,那么您可能还想在测试中考虑模拟,因为您很想集中精力于一段代码。模拟是在其中注入对象依赖项的对象,但是注入的对象不是“真实”或生产对象。它们是具有硬编码行为的伪对象,可以更轻松地隔离行为错误。Rhino.Mocks是一个流行的免费模拟框架,该框架实际上将为您编写对象。TypeMock.NET(提供社区版本的商业产品)是一个功能更强大的框架,可以模拟CLR对象。例如,在测试数据库应用程序时,对模拟SqlConnection / SqlCommand和Datatable类非常有用。
希望这个答案会给您更多的信息,以使您大致了解单元测试,并帮助您从单元测试中获得更好的结果。
这个问题已经进入晚期,但我想我会分享自己的方式。
基本上,我将所有我的单元测试类放在程序集中,它们都在该程序集的“默认”下方的“ UnitTest”命名空间中进行测试-每个测试文件都包装在:
#if DEBUG
...test code...
#endif
阻止,而所有这些都意味着a)不在发行版本中分发,b)我可以使用internal
/Friend
level声明而不会发生箍跳。
与该问题更相关的是,此方法提供的另一件事是使用partial
类,这些类可用于创建用于测试私有方法的代理,例如,用于测试类似私有方法的对象,该代理返回整数值:
public partial class TheClassBeingTested
{
private int TheMethodToBeTested() { return -1; }
}
在程序集的主要类和测试类中:
#if DEBUG
using NUnit.Framework;
public partial class TheClassBeingTested
{
internal int NUnit_TheMethodToBeTested()
{
return TheMethodToBeTested();
}
}
[TestFixture]
public class ClassTests
{
[Test]
public void TestMethod()
{
var tc = new TheClassBeingTested();
Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
}
}
#endif
显然,您需要确保在开发过程中不要使用此方法,尽管如果您使用Release版本,它将很快表示对其进行了无意的调用。
单元测试的主要目标是测试类的公共方法。这些公共方法将使用那些私有方法。单元测试将测试公开可用内容的行为。
您不测试私有功能。有多种方法可以使用反射来进入私有方法和属性。但这并不是一件容易的事,我强烈反对这种做法。
您根本不应该测试任何不公开的东西。
如果您有一些内部方法和属性,则应考虑将其更改为公开状态,或将其随应用程序一起交付测试(这并不是我真正认为的问题)。
如果您的客户能够运行Test-Suite,并且看到您提供的代码实际上是“有效的”,那么我认为这不是问题(只要您不通过它放弃IP)。我在每个版本中包括的内容是测试报告和代码覆盖率报告。
理论上,单元测试仅应测试合同。即只有班上的公众成员。但实际上,开发人员通常希望测试内部成员。-而且还不错。是的,它与理论背道而驰,但在实践中有时可能有用。
因此,如果您真的想测试内部成员,则可以使用以下方法之一:
代码示例(伪代码):
public class SomeClass
{
protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{
protected void SomeMethod2() {}
[Test]
public void SomeMethodTest() { SomeMethod2(); }
}
Message: Method is not public
。