111

我想知道如何正确使用 NUnit。首先,我创建了一个单独的测试项目,它使用我的主项目作为参考。但在那种情况下,我无法测试私有方法。我的猜测是我需要将我的测试代码包含到我的主代码中?!- 这似乎不是正确的方法。(我不喜欢发送带有测试的代码的想法。)

你如何用 NUnit 测试私有方法?

4

13 回答 13

74

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

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

于 2008-10-30T11:23:31.223 回答
67

虽然我同意单元测试的重点应该是公共接口,但如果你也测试私有方法,你会对你的代码有更细致的印象。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。上面的例子只是实例方法。

于 2012-01-27T15:08:41.180 回答
48

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

如果你发现你有很多私有方法要测试,通常这表明你应该重构你的代码。

将这些方法公开在它们当前所在的类上是错误的。这将违反您希望该班级拥有的合同。

将它们移到辅助类并在那里公开它们可能是正确的。您的 API 可能不会公开此类。

这种方式测试代码永远不会与您的公共代码混合。

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

于 2008-10-30T11:43:56.893 回答
21

可以通过将您的测试程序集声明为您正在测试的目标程序集的友元程序集来测试私有方法。有关详细信息,请参阅下面的链接:

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

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

尽管如此,您确实不需要测试私有方法。您不仅希望将代码重构为更小的构建块。当您开始重构时,一个可能对您有所帮助的技巧是尝试考虑与您的系统相关的域,并考虑居住在该域中的“真实”对象。您系统中的对象/类应直接与真实对象相关,这将允许您隔离对象应包含的确切行为并限制对象职责。这将意味着您在逻辑上进行重构,而不仅仅是为了使测试特定方法成为可能;您将能够测试对象的行为。

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

希望这个答案能给你更多的信息,让你了解一般的单元测试,并帮助你从单元测试中获得更好的结果。

于 2008-10-30T12:02:09.897 回答
6

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

单元测试已经发展为测试驱动的开发。具有测试所有方法的能力对于该应用程序很有用。

于 2013-07-16T19:47:15.530 回答
5

这个问题是在它的高级年,但我想我会分享我这样做的方式。

基本上,我的所有单元测试类都在他们正在测试的程序集中在该程序集的“默认”下方的“UnitTest”命名空间中 - 每个测试文件都包含在:

#if DEBUG

...test code...

#endif

块,所有这一切意味着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 版本很快就会表明对它的无意调用。

于 2014-09-19T07:59:14.900 回答
4

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

于 2008-10-30T11:25:11.610 回答
3

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

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

于 2011-05-02T18:13:06.147 回答
2

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

你根本不应该测试任何不公开的东西。

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

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

于 2008-10-30T11:33:32.187 回答
2

在单元测试的理论中,只需要测试合约。即只有班级的公共成员。但在实践中,开发人员通常也想测试内部成员 - 这还不错。是的,它与理论背道而驰,但在实践中它有时会很有用。

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

  1. 公开您的会员。在许多书中,作者建议这种方法很简单
  2. 您可以让您成为内部成员并将InternalVisibleTo添加到程序集
  3. 您可以保护类成员并从您的测试类继承您的测试类。

代码示例(伪代码):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{
    
    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}
于 2016-11-03T08:55:43.237 回答
1

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

因此,不!您无法访问私有方法,但这是一种解决方法。

于 2016-05-31T17:59:41.250 回答
1

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

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
于 2020-09-07T10:58:52.683 回答
0

我会让私有方法包可见。这样你就可以保持它相当私密,同时仍然能够测试这些方法。我不同意人们说公共接口是唯一应该测试的接口。私有方法中经常有非常关键的代码,仅通过外部接口无法正确测试。

所以它真的归结为如果你更关心正确的代码或信息隐藏。我想说包可见性是一个很好的折衷方案,因为为了访问这些方法,有人必须将他们的类放在你的包中。这真的应该让他们三思而后行,看看这是否真的很聪明。

顺便说一句,我是一个 Java 人,所以包 visiblilty 在 C# 中可能被称为完全不同的东西。可以说,两个类必须在同一个命名空间中才能访问这些方法。

于 2008-10-30T11:55:17.853 回答