【发布时间】:2010-09-19 23:23:48
【问题描述】:
我想知道如何正确使用 NUnit。首先,我创建了一个单独的测试项目,它使用我的主项目作为参考。但在那种情况下,我无法测试私有方法。我的猜测是我需要将我的测试代码包含到我的主代码中?! - 这似乎不是正确的方法。 (我不喜欢发送带有测试的代码的想法。)
如何使用 NUnit 测试私有方法?
【问题讨论】:
标签: c# unit-testing testing nunit
我想知道如何正确使用 NUnit。首先,我创建了一个单独的测试项目,它使用我的主项目作为参考。但在那种情况下,我无法测试私有方法。我的猜测是我需要将我的测试代码包含到我的主代码中?! - 这似乎不是正确的方法。 (我不喜欢发送带有测试的代码的想法。)
如何使用 NUnit 测试私有方法?
【问题讨论】:
标签: c# unit-testing testing nunit
一般来说,单元测试针对的是类的公共接口,理论上实现并不重要,只要从客户的角度来看结果是正确的。
因此,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/en-us/library/0tke9fxk.aspx
这很有用,因为它主要将测试代码与生产代码分开。我自己从来没有使用过这种方法,因为我从来没有发现需要它。我想您可以使用它来尝试和测试您根本无法在测试环境中复制的极端测试用例,以查看您的代码如何处理它。
正如已经说过的,您确实不需要测试私有方法。您不仅希望将代码重构为更小的构建块。当您开始重构时,一个可能对您有所帮助的技巧是尝试考虑与您的系统相关的域,并考虑居住在该域中的“真实”对象。您系统中的对象/类应直接与真实对象相关,这将允许您隔离对象应包含的确切行为并限制对象职责。这将意味着您在逻辑上进行重构,而不仅仅是为了使测试特定方法成为可能;您将能够测试对象的行为。
如果您仍然觉得需要进行内部测试,那么您可能还需要考虑在测试中进行模拟,因为您可能希望专注于一段代码。模拟是将对象依赖项注入其中,但注入的对象不是“真实”或生产对象。它们是具有硬编码行为的虚拟对象,以便更容易隔离行为错误。 Rhino.Mocks 是一个流行的免费模拟框架,它基本上会为您编写对象。 TypeMock.NET(提供社区版的商业产品)是一个更强大的框架,可以模拟 CLR 对象。对于模拟 SqlConnection/SqlCommand 和 Datatable 类非常有用,例如在测试数据库应用程序时。
希望这个答案能给您提供更多信息,让您大致了解单元测试,并帮助您从单元测试中获得更好的结果。
【讨论】:
我赞成拥有测试私有方法的能力。当 xUnit 启动时,它的目的是在编写代码后测试功能。为此目的,测试接口就足够了。
单元测试已经发展为测试驱动的开发。具有测试所有方法的能力对于该应用程序很有用。
【讨论】:
这个问题是在它的高级年,但我想我会分享我这样做的方式。
基本上,我的所有单元测试类都在他们正在测试的程序集中的“UnitTest”命名空间中,该名称空间位于该程序集的“默认”下方 - 每个测试文件都包含在:
#if DEBUG
...test code...
#endif
block,所有这一切都意味着 a) 它没有在发行版中分发 b) 我可以使用 internal/Friend 级别声明而无需跳跃。
这提供的另一件事,与这个问题更相关,是使用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 版本很快就会表明对它的无意调用。
【讨论】:
单元测试的主要目标是测试类的公共方法。那些公共方法将使用那些私有方法。单元测试将测试公开内容的行为。
【讨论】:
抱歉,如果这不能回答问题,但使用反射、#if #endif 语句或使私有方法可见等解决方案不能解决问题。不让私有方法可见可能有几个原因......例如,如果它是生产代码并且团队正在回顾性地编写单元测试怎么办。
对于我正在处理的项目,只有 MSTest(遗憾的是)似乎有办法使用访问器对私有方法进行单元测试。
【讨论】:
您不测试私有函数。 有一些方法可以使用反射来进入私有方法和属性。但这并不容易,我强烈反对这种做法。
你根本不应该测试任何不公开的东西。
如果您有一些内部方法和属性,您应该考虑将其更改为公开,或者将您的测试与应用程序一起发布(我认为这不是问题)。
如果您的客户能够运行测试套件并看到您交付的代码实际上是“工作”,我不认为这是一个问题(只要您不通过这个泄露您的 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。
你可以让你的方法在内部受到保护,然后使用
assembly: InternalsVisibleTo("NAMESPACE")
到您的测试命名空间。
因此,不!您无法访问私有方法,但这是一种解决方法。
【讨论】:
如果你需要访问类的非静态私有方法,可以试试这个:
class Foo
{
private int Sum(int num1, int num2)
{
return num1 + num2;
}
}
MethodInfo sumPrivate =
typeof(Foo).GetMethod("Sum", BindingFlags.NonPublic | BindingFlags.Instance);
int sum = (int)sumPrivate.Invoke(new Foo(), new object[] { 2, 5 });
// 7
【讨论】:
我会让私有方法包可见。这样你就可以保持它相当私密,同时仍然能够测试这些方法。我不同意人们说公共接口是唯一应该测试的接口。私有方法中经常有非常关键的代码,仅通过外部接口无法正确测试。
所以归根结底是您是否更关心正确的代码或信息隐藏。我想说包可见性是一个很好的折衷方案,因为为了访问这些方法,有人必须将他们的类放在你的包中。这真的应该让他们三思而后行,看看这是否真的很聪明。
顺便说一句,我是一个 Java 人,所以包 visiblilty 在 C# 中可能被称为完全不同的东西。可以说,两个类必须在同一个命名空间中才能访问这些方法。
【讨论】: