关于c#:如何使用NUnit测试私有方法?

How do you test private methods with NUnit?

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

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


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

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


虽然我同意单元测试的重点应该是公共界面,但如果您也测试私有方法,则会对代码产生更为细致的印象。 MS测试框架允许通过使用PrivateObject和PrivateType,NUnit不允许。我做的是:

1
2
3
4
5
6
7
8
9
10
11
12
13
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启动时,它用于在编写代码后测试功能。测试界面就足够了。

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


这个问题已经过了很多年,但我想我会分享这样做的方式。

基本上,我在程序集中的所有单元测试类都在他们在该程序集的'default'下面的'UnitTest'命名空间中进行测试 - 每个测试文件都包含在:

1
2
3
4
5
#if DEBUG

...test code...

#endif

块,所有这些意味着a)它没有在发布中分发,而b)我可以使用internal / Friend级别的声明,而不需要跳环。

与此问题更相关的另一个问题是使用partial类,它可用于创建用于测试私有方法的代理,例如测试类似于返回整数值的私有方法:

1
2
3
4
public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

在程序集的主要类和测试类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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(遗憾地)似乎有办法使用访问器来单元测试私有方法。


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

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

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

如果您的客户能够运行Test-Suite并且看到您提供的代码实际上"正常",我认为这不是问题(只要您不通过此方式泄露您的IP)。我在每个版本中包含的内容都是测试报告和代码覆盖率报告。


在单元测试理论中,只应测试合同。即只有班级的公共成员。但实际上开发人员通常希望测试内部成员。 - 而且还不错。是的,这违背了理论,但在实践中它有时是有用的。

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

  • 让您的会员公开。在许多书中,作者提出了这一点
    方法很简单
  • 您可以在内部创建成员并将InternalVisibleTo添加到
    assebly
  • 您可以使类成员受到保护并继承您的测试类
    来自您的测试类。
  • 代码示例(伪代码):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class SomeClass
    {
        protected int SomeMethod() {}
    }
    [TestFixture]
    public class TestClass : SomeClass{

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


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

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


    我会使私有方法包可见。这样你就可以保持合理的私密性,同时还能够测试这些方法。我不同意人们说公共接口是唯一应该测试的接口。私有方法中通常存在非常关键的代码,只能通过外部接口进行适当的测试。

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

    我是一个Java家伙,所以包装visiblilty在C#中可能被称为完全不同的东西。可以说,当两个类必须位于同一名称空间中才能访问这些方法。