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

MrF*_*Fox 100 c# testing nunit unit-testing

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

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

har*_*rpo 73

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

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

  • 我完全不同意这个论点.单元测试不是关于类,而是关于*代码*.如果我有一个带有一个公共方法的类,而十个私有用于创建公共方法的结果,我无法确保每个私有方法以预期的方式工作.我可以尝试在公共方法上创建测试用例,以正确的方式命中正确的私有方法.但我不知道私有方法是否实际被调用,除非我相信公共方法永远不会改变.私有方法旨在私有代码的生产用户,而不是作者. (127认同)
  • 私有方法是一个实现细节.具有单元测试的许多方面是允许重构*实现*而不改变*行为*.如果私有方法变得如此复杂以至于人们希望单独使用测试来覆盖它们,那么可以使用这个引理:"私有方法总是可以是单独类的公共(或内部)方法".也就是说,如果一个逻辑足够先进以进行测试,它可能是自己的类,有自己的测试. (22认同)
  • @Quango,在标准测试设置中,"作者"*是代码的生产用户.然而,如果您没有闻到设计问题,您可以选择在不改变类的情况下测试非公共方法.`System.Reflection`允许您使用绑定标志访问和调用非公共方法,因此您可以破解NUnit或设置自己的框架.或者(我认为更简单),您可以设置编译时标志(#if TESTING)来更改访问修饰符,允许您使用现有框架. (3认同)
  • @AndersForsgren但_unit_测试的意义,而不是_integration_或_functional_测试,正是为了测试这些细节.并且代码覆盖被认为是一个重要的单元测试指标,因此理论上_每一段逻辑都"足够先进,不受测试". (3认同)

小智 52

虽然我同意单元测试的重点应该是公共界面,但如果您也测试私有方法,则会对代码产生更为细致的印象.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;
}
Run Code Online (Sandbox Code Playgroud)

这种方式意味着您不必牺牲封装来支持可测试性.请记住,如果要测试私有静态方法,则需要修改BindingFlags.以上示例仅用于方法.

  • 这正是我所需要的.谢谢!我只需要检查私有字段是否设置正确,我不需要花3个小时来弄清楚如何通过公共接口确定.这是现有的代码,不能随心所欲地重构.一个简单的问题可以用理想主义的教条来回答这很有趣...... (11认同)
  • 测试那些小辅助方法可以捕获单元测试中的单元部分.并非所有都适用于实用工具类. (7认同)
  • 同意.选择的答案有其优点,但答案是"我应该测试私人方法",而不是OP的问题. (6认同)
  • 这应该是选定的答案,因为它是唯一一个实际回答问题的答案. (5认同)
  • 它的确有效.谢谢!这是许多人正在寻找的.这应该是选定的答案, (2认同)

mor*_*lli 46

编写单元测试的常见模式是仅测试公共方法.

如果您发现有许多要测试的私有方法,通常这表示您应该重构代码.

在目前居住的班级上公开这些方法是错误的.那会打破你希望那个班级拥有的合同.

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

这样,测试代码永远不会与您的公共代码混合在一起.

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

  • 因此,如果你有一个私有方法,在一个类中的多个地方被调用来做一些非常特定的东西,只需要该类,并且它不会作为公共API的一部分公开...你说它把它放在一个帮助器中上课只是为了测试?你也可以把它公之于众. (4认同)

Daf*_*ins 20

可以通过将测试程序集声明为正在测试的目标程序集的友元程序集来测试私有方法.请参阅以下链接了解详情:

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

这可能很有用,因为它主要是从您的生产代码中分离您的测试代码.我自己从未使用过这种方法,因为我从未发现需要它.我想您可以使用它来尝试测试极端测试用例,这些测试用例在测试环境中无法复制,以查看代码如何处理它.

如前所述,你真的不需要测试私有方法.你不仅仅想要将代码重构为更小的构建块.当您进行重构时,可能会帮助您的一个提示是尝试思考您的系统所涉及的域,并考虑居住在该域中的"真实"对象.系统中的对象/类应直接与真实对象相关,这将允许您隔离对象应包含的确切行为,并限制对象的职责.这意味着您在逻辑上进行重构,而不仅仅是为了测试特定方法; 您将能够测试对象的行为.

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

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

  • 可以测试**内部**方法,而不是私有(使用NUnit). (12认同)

Mar*_*ass 6

我赞成有能力测试私有方法.当xUnit启动时,它用于在编写代码后测试功能.测试界面就足够了.

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


Stu*_*ood 5

这个问题已经进入高级阶段,但我想我会分享我的做法。

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

#if DEBUG

...test code...

#endif
Run Code Online (Sandbox Code Playgroud)

块,所有这一切都意味着 a) 它不会在发布中分发,并且 b) 我可以使用internal/ Friendlevel 声明而无需跳箍。

这提供的另一件事与这个问题更相关,是使用partial类,它可用于创建用于测试私有方法的代理,例如测试诸如返回整数值的私有方法之类的东西:

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}
Run Code Online (Sandbox Code Playgroud)

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

#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
Run Code Online (Sandbox Code Playgroud)

显然,您需要确保在开发时不使用此方法,尽管如果您这样做,Release 构建很快就会指示对它的无意调用。