How do you test private methods with NUnit?

I am wondering how to use NUnit correctly. First, I created a separate test project that uses my main project as reference. But in that case, I am not able to test private methods. My guess was that I need to include my test code into my main code?! - That doesn't seem to be the correct way to do it. (I dislike the idea of shipping code with tests in it.)

How do you test private methods with NUnit?

66283 次浏览

通常,单元测试处理一个类的公共接口,理论上只要结果从客户的角度来看是正确的,实现是无关紧要的。

因此,NUnit 不提供任何测试非公共成员的机制。

单元测试的主要目标是测试类的公共方法。那些公共方法将使用那些私有方法。单元测试将测试公开可用的行为。

不测试私有函数。 有一些使用反射进入私有方法和属性的方法。但这并不容易,我强烈反对这种做法。

You simply shouldn't test anything that's not public.

如果您有一些内部的方法和属性,您应该考虑将其更改为 public,或者将您的测试与应用程序一起发布(我并不认为这是一个问题)。

如果您的客户能够运行一个测试套件,并看到您提供的代码实际上是“工作的”,我不认为这是一个问题(只要您不通过这种方式放弃您的 IP)。我在每个版本中都包含了测试报告和代码覆盖报告。

编写单元测试的一种常见模式是只测试公共方法。

如果您发现有许多要测试的私有方法,通常这是您应该重构代码的标志。

将这些方法公开在它们当前所在的类上是错误的。 这会违反你想让那个类拥有的契约。

将它们移动到一个 helper 类并在那里将它们公开可能是正确的。 此类可能不会由您的 API 公开。

This way test code is never mixed with your public code.

类似的问题是测试私有类,即不从程序集导出的类。 在这种情况下,可以使用 InternalsVisibleTo 属性显式地使测试代码程序集成为生产代码程序集的朋友。

我将使私有方法包可见。这样,您可以在测试这些方法的同时保持它的合理私有性。我不同意有人说公共接口是唯一需要测试的接口。私有方法中经常存在真正关键的代码,这些代码无法通过仅通过外部接口进行正确测试。

因此,归根结底,如果您更关心正确的代码或信息隐藏。我认为包可见性是一个很好的折衷方案,因为为了访问这些方法,有些人必须将他们的类放在您的包中。这真的应该让他们三思,这是否是一个真正明智的做法。

顺便说一下,我是一个 Java 爱好者,所以包可见性在 C # 中可能被称为完全不同的东西。可以说,当两个类必须位于相同的名称空间中才能访问这些方法时就足够了。

可以通过将测试程序集声明为正在测试的目标程序集的朋友程序集来测试私有方法。详情请参阅以下连结:

Http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

这很有用,因为它基本上将测试代码与生产代码分离开来。我自己从来没有用过这种方法,因为我从来没有发现需要它。我假设您可以使用它来尝试和测试那些您无法在测试环境中复制的极端测试用例,以查看您的代码是如何处理它的。

不过,正如前面所说的,您确实不应该需要测试私有方法。您更希望将代码重构为更小的构建块。在进行重构时,有一个技巧可能会对您有所帮助,那就是尝试思考与您的系统相关的领域,并思考驻留在该领域中的“真实”对象。您系统中的对象/类应该直接与实际对象相关,这将允许您隔离对象应该包含的确切行为,并限制对象的责任。这将意味着您正在进行逻辑重构,而不仅仅是为了测试某个特定的方法; 您将能够测试对象的行为。

如果您仍然觉得需要进行内部测试,那么您可能还需要考虑在测试中进行嘲讽,因为您可能希望将重点放在一段代码上。Mocking 是将一个对象依赖项注入其中,但注入的对象不是“真正的”或生产对象。它们是具有硬编码行为的虚拟对象,以便于隔离行为错误。犀牛。Mocks 是一个流行的免费 Mocking 框架,它基本上可以为您编写对象。NET (一个提供社区版本的商业产品)是一个更强大的框架,它可以模拟 CLR 对象。例如,在测试数据库应用程序时,对 SqlConnection/SqlCommand 和 Datatable 类进行模拟非常有用。

希望这个答案能够为您提供更多有关单元测试的信息,并帮助您从单元测试中获得更好的结果。

如果这不能回答问题,但是使用反射等解决方案,# if # endf 语句或者使私有方法可见并不能解决问题,那么我道歉。有几个原因使私有方法不可见... ... 例如,如果它是生产代码,而团队正在回顾性地编写单元测试会怎样。

对于我正在进行的项目,只有 MSTest (遗憾的是)似乎有一种方法,使用访问器,单元测试私有方法。

While I agree that the focus of unit testing should be the public interface, you get a far more granular impression of your code if you test private methods as well. The MS testing framework allows for this through the use of PrivateObject and PrivateType, NUnit does not. What I do instead is:

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。上面的示例只是用于实例方法。

我赞成拥有测试私有方法的能力。当 xUnit 启动时,它是为了在编写代码之后测试功能。测试接口就足够了。

Unit testing has evolved to test-driven development. Having the capability to test all methods is useful for that application.

这个问题已经有些年头了,但是我想我应该分享一下我的方法。

基本上,我把我所有的单元测试类放在他们正在测试的程序集中,在该程序集的“默认”下面的“ UnitTest”名称空间中——每个测试文件都包装在一个:

#if DEBUG


...test code...


#endif

block, and all of that means that a) it's not being distributed in a release and b) I can use internal/Friend level declarations without hoop jumping.

另一个与这个问题更相关的事情是使用 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

显然,您需要确保在开发时不使用这个方法,但是如果您使用了这个方法,那么发布版本很快就会指示您无意中调用它。

您可以使您的方法在内部受到保护,然后使用 assembly: InternalsVisibleTo("NAMESPACE") 到您的测试命名空间。

因此,不! 您不能访问私有方法,但这是一个解决方案。

在单元测试理论中,只测试合同。也就是说,只有类的公共成员。但在实践中,开发人员通常也想测试内部成员——这并不坏。是的,它违背了理论,但在实践中,它有时是有用的。

因此,如果你真的想测试内部成员,你可以使用以下方法之一:

  1. 让你的会员公开。在许多书中,作者建议这样做 尽量简单
  2. 可以将成员设置为内部成员,并将 InternalVisibleTo添加到 集合
  3. 可以使类成员受到保护并继承测试类 从你的班级被测试。

代码示例(伪代码) :

public class SomeClass
{
protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{
    

protected void SomeMethod2() {}
[Test]
public void SomeMethodTest() { SomeMethod2(); }
}

如果需要访问类的非静态私有方法,可以尝试这样做:

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