简体   繁体   English

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

[英]How do you test private methods with NUnit?

I am wondering how to use NUnit correctly.我想知道如何正确使用 NUnit。 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?如何使用 NUnit 测试私有方法?

Generally, unit testing addresses a class's public interface, on the theory that the implementation is immaterial, so long as the results are correct from the client's point of view.一般来说,单元测试处理类的公共接口,理论上实现是无关紧要的,只要结果从客户端的角度来看是正确的。

So, NUnit does not provide any mechanism for testing non-public members.因此,NUnit 不提供任何测试非公共成员的机制。

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. MS 测试框架通过使用 PrivateObject 和 PrivateType 来实现这一点,而 NUnit 则不允许。 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;
}

This way means you don't have to compromise encapsulation in favour of testability.这种方式意味着您不必为了可测试性而妥协封装。 Bear in mind you'll need to modify your BindingFlags if you want to test private static methods.请记住,如果要测试私有静态方法,则需要修改 BindingFlags。 The above example is just for instance methods.上面的例子只是实例方法。

A common pattern for writing unit tests is to only test public methods.编写单元测试的常见模式是只测试公共方法。

If you find that you have many private methods that you want to test, normally this is a sign that you should refactor your code.如果您发现有许多要测试的私有方法,通常这表明您应该重构代码。

It would be wrong to make these methods public on the class where they currently live.在它们当前所在的类上公开这些方法是错误的。 That would break the contract that you want that class to have.这将破坏您希望该课程拥有的合同。

It may be correct to move them to a helper class and make them public there.将它们移动到辅助类并在那里公开它们可能是正确的。 This class may not be exposed by your API.您的 API 可能不会公开此类。

This way test code is never mixed with your public code.这样测试代码永远不会与您的公共代码混合。

A similar problem is testing private classes ie.一个类似的问题是测试私有类,即。 classes you do not export from your assembly.不从程序集中导出的类。 In this case you can explicitly make your test code assembly a friend of the production code assembly using the attribute InternalsVisibleTo.在这种情况下,您可以使用 InternalsVisibleTo 属性显式地使您的测试代码程序集成为生产代码程序集的朋友。

It is possible to test private methods by declaring your test assembly as a friend assembly of the target assembly you are testing.可以通过将您的测试程序集声明为您正在测试的目标程序集的朋友程序集来测试私有方法。 See the link below for details:详情请参阅以下链接:

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

This can be useful as it does mostly seperate your test code from your production code.这很有用,因为它主要将测试代码与生产代码分开。 I have never used this method myself as i have never found a need for it.我自己从未使用过这种方法,因为我从未发现需要它。 I suppose that you could use it to try and test extreme test cases which you simply can't replicate in your test environment to see how your code handles it.我想你可以用它来尝试测试极端的测试用例,你根本无法在测试环境中复制这些用例来查看代码如何处理它。

As has been said though, you really shouldn't need to test private methods.如前所述,您确实不需要测试私有方法。 You more than likley want to refactor your code into smaller building blocks.您非常想将您的代码重构为更小的构建块。 One tip that might help you when you come to refactor is to try and think about the domain that your system relates to and think about the 'real' objects that inhabit this domain.当您开始重构时,一个可能对您有帮助的技巧是尝试考虑您的系统相关的域并考虑驻留在该域中的“真实”对象。 Your objects/classes in your system should relate directly to a real object which will allow you to isolate the exact behaviour that the object should contain and also limit the objects responsibilities.您系统中的对象/类应该与真实对象直接相关,这将允许您隔离对象应包含的确切行为并限制对象职责。 This will mean that you are refactoring logically rather than just to make it possible to test a particular method;这意味着您正在逻辑重构,而不仅仅是为了测试特定方法; you will be able to test the objects behaviour.您将能够测试对象的行为。

If you still feel the need to test internal then you might also want to consider mocking in your testing as you are likley to want to focus on one piece of code.如果您仍然觉得需要进行内部测试,那么您可能还需要考虑在测试中进行模拟,因为您可能希望专注于一段代码。 Mocking is where you inject an objects dependencies into it but the objects injected are not the 'real' or production objects.模拟是您将对象依赖项注入其中但注入的对象不是“真实”或生产对象的地方。 They are dummy objects with hardcoded behaviour to make it easier to isolate behavioural errors.它们是具有硬编码行为的虚拟对象,可以更轻松地隔离行为错误。 Rhino.Mocks is a popular free mocking framework which will essentially write the objects for you. Rhino.Mocks 是一个流行的免费模拟框架,它基本上会为您编写对象。 TypeMock.NET (a commercial product with a community edition available) is a more powerful framework which can mock CLR objects. TypeMock.NET(提供社区版的商业产品)是一个更强大的框架,可以模拟 CLR 对象。 Very useful for mocking the SqlConnection/SqlCommand and Datatable classes for instance when testing a database app.例如在测试数据库应用程序时模拟 SqlConnection/SqlCommand 和 Datatable 类非常有用。

Hopefully this answer will give you a bit more information to inform you about Unit Testing in general and help you get better results from Unit Testing.希望这个答案会给你更多的信息,让你了解一般的单元测试,并帮助你从单元测试中获得更好的结果。

I'm in favor of having the capability to test private methods.我赞成有能力测试私有方法。 When xUnit started it was intended for testing functionality after the code was written.当 xUnit 启动时,它用于在编写代码后测试功能。 Testing the interface is sufficient for this purpose.为此目的,测试接口就足够了。

Unit testing has evolved to test-driven development.单元测试已经演变为测试驱动的开发。 Having the capability to test all methods is useful for that application.能够测试所有方法对于该应用程序很有用。

This question is in its advanced years, but I thought I'd share my way of doing this.这个问题已经进入高级阶段,但我想我会分享我的做法。

Basically, I have all my unit test classes in the assembly they're testing in a 'UnitTest' namespace below the 'default' for that assembly - each test file is wrapped in a:基本上,我在程序集中有我所有的单元测试类,它们在该程序集的“默认”下方的“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.阻止,所有这一切意味着 a) 它不会在发布中分发,并且 b) 我可以使用internal / Friend级别的声明而无需跳箍。

The other thing this offers, more pertinent to this question, is the use of partial classes, which can be used to create a proxy for testing private methods, so for example to test something like a private method which returns an integer value:这提供的另一件事与这个问题更相关,是使用partial类,它可用于创建用于测试私有方法的代理,例如测试诸如返回整数值的私有方法之类的东西:

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

in the main classes of the assembly, and the test class:在程序集的主要类和测试类中:

#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

Obviously, you need to ensure that you don't use this method while developing, though a Release build will soon indicate an inadvertent call to it if you do.显然,您需要确保在开发时不使用此方法,尽管如果您这样做,Release 构建很快就会指示对它的无意调用。

The main goal of unit testing is to test the public methods of a class.单元测试的主要目标是测试类的公共方法。 Those public methods will use those private methods.那些公共方法将使用那些私有方法。 Unit testing will test the behavior of what is publicly available.单元测试将测试公开可用的行为。

Apologies if this doesn't answer the question but solutions like using reflection, #if #endif statements or making private methods visible does not solve the problem.抱歉,如果这不能回答问题,但使用反射、#if #endif 语句或使私有方法可见等解决方案并不能解决问题。 There can be several reasons for not making private methods visible... what if it's production code and the team is retrospectively writing unit tests for example.不使私有方法可见可能有几个原因……例如,如果它是生产代码并且团队正在回顾性地编写单元测试怎么办。

For the project that I am working on only MSTest (sadly) appears to have a way, using accessors, to unit test private methods.对于我正在处理的项目,只有 MSTest(遗憾的是)似乎有办法使用访问器来对私有方法进行单元测试。

You don't test private functions.您不测试私有函数。 There are ways to use reflection to get into private methods and properties.有一些方法可以使用反射来访问私有方法和属性。 But that isn't really easy and I strongly discourage this practice.但这并不容易,我强烈反对这种做法。

You simply shouldn't test anything that's not public.你根本不应该测试任何非公开的东西。

If you have some internal methods and properties, you should consider either changing that to public, or to ship your tests with the app (something I don't really see as a problem).如果您有一些内部方法和属性,您应该考虑将其更改为 public,或者将您的测试与应用程序一起发送(我认为这不是问题)。

If your customer is able to run a Test-Suite and see that the code you delivered is actually "working", I don't see this as a problem (as long as you don't give away your IP through this).如果您的客户能够运行测试套件并看到您提供的代码实际上“有效”,我认为这不是问题(只要您不通过此方式泄露您的 IP)。 Things I include in every release are test-reports and code coverage reports.我在每个版本中包含的内容是测试报告和代码覆盖率报告。

In theory of Unit Testing, only contract should be tested.在单元测试的理论中,只应测试合约。 ie only public members of the class.即只有类的公共成员。 But in practice, developer usually wants to test internal members too - and it is not bad.但在实践中,开发人员通常也想测试内部成员——而且还不错。 Yes, it goes against the theory, but in practice it can be useful sometimes.是的,它与理论背道而驰,但在实践中它有时会很有用。

So if you really want to test internal members, you can use one of these approaches:所以如果你真的想测试内部成员,你可以使用以下方法之一:

  1. Make your member public.公开您的会员。 In many books, authors suggest this approach as simple在许多书中,作者认为这种方法很简单
  2. You can make you members internal and add InternalVisibleTo to assembly您可以让您成为内部成员并将InternalVisibleTo添加到程序集
  3. You can make class members protected and inherit your test class from your class under test.您可以保护类成员并从您的测试类继承您的测试类。

Code example (pseudo code):代码示例(伪代码):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{
    
    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}

You can make your methods protected internal, and then using assembly: InternalsVisibleTo("NAMESPACE") to your testing namespace.您可以将您的方法设置为内部保护,然后使用assembly: InternalsVisibleTo("NAMESPACE")到您的测试命名空间。

Hence, NO!因此,不! You cannot access private methods, but this is a work-around.您无法访问私有方法,但这是一种解决方法。

I would make the private methods package visible.我会让私有方法包可见。 That way you keep it reasonably private while still being able to test those methods.这样你就可以在保持合理的私密性的同时仍然能够测试这些方法。 I don't agree with the people saying that the public interfaces are the only ones that should be tested.我不同意人们所说的公共接口是唯一应该测试的接口。 There is often really critical code in the private methods that can't be properly tested by only going through the external interfaces.私有方法中通常存在非常关键的代码,仅通过外部接口无法对其进行正确测试。

So it really boils down to if you care more about correct code or information hiding.所以这真的归结为你是否更关心正确的代码或信息隐藏。 I'd say package visibility is a good compromise since in order to access those method someone would have to place their class in your package.我想说包可见性是一个很好的折衷方案,因为为了访问这些方法,有人必须将他们的类放在你的包中。 That should really make them think twice about whether that is a really smart thing to do.这真的应该让他们三思而后行,这是否是一件真正明智的事情。

I'm a Java guy btw, so package visiblilty might be called something entirely different in C#.顺便说一句,我是一个 Java 人,所以包可见性在 C# 中可能被称为完全不同的东西。 Suffice to say that it's when two classes have to be in the same namespace in order to access those methods.可以说,当两个类必须在同一个命名空间中才能访问这些方法时就足够了。

If you need to access a non-static private method of class, could try this:如果您需要访问类的非静态私有方法,可以试试这个:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM