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

时间:2008-10-30 11:20:26

标签: c# unit-testing testing nunit

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

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

13 个答案:

答案 0 :(得分:69)

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

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

答案 1 :(得分:47)

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

答案 2 :(得分:44)

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

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

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

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

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

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

答案 3 :(得分:21)

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

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

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

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

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

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

答案 4 :(得分:6)

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

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

答案 5 :(得分:4)

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

答案 6 :(得分:4)

这个问题已进入高级阶段,但我认为我会分享这样做的方式。

基本上,我在程序集中测试了所有单元测试类,它们在该程序集的'default'下面的'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版本很快就会表示无意中调用它。

答案 7 :(得分:3)

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

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

答案 8 :(得分:2)

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

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

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

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

答案 9 :(得分:1)

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

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

答案 10 :(得分:1)

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

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

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

    Sub LoopThroughDirectory()
    Dim MyFile As String
    Dim erow
    Dim Filepath As String
    Dim lr As Long
    Dim mainName As String
    
    mainName = ActiveWorkbook.Name
    Filepath = "C:\Users\Joel\Desktop\MultiFileTesting\"
    MyFile = Dir(Filepath)
    Do While Len(MyFile) > 0
        If MyFile <> "TestingMaster.xlsm" And Right(MyFile, 4) = "xlsm" Then
            Workbooks.Open (Filepath & MyFile)
            lastrow = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
            lastcolumn = Sheets(1).Cells(1, Columns.Count).End(xlToLeft).Column
            Sheets(1).Range(Cells(2, 1), Cells(lastrow, lastcolumn)).Copy
            Windows(mainName).Activate
            erow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
            Sheet1.Range(Cells(erow, 1), Cells(erow, 11)).PasteSpecial xlPasteAll
            Workbooks(MyFile).Close False
        End If
    
        MyFile = Dir
    Loop
    End Sub
    

答案 11 :(得分:0)

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

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

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

答案 12 :(得分:0)

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

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