我正在构建一个类库,它将有一些公共和私有方法。我希望能够对私有方法进行单元测试(主要是在开发过程中,但也可以用于将来的重构)。

正确的做法是什么?


当前回答

这里有一篇关于私有方法单元测试的好文章。但是我不知道哪个更好,是让你的应用程序专门为测试而设计(就像只为测试而创建测试)还是使用反射来测试。 我敢肯定大多数人都会选择第二种方式。

其他回答

I don't agree with the "you should only be interested in testing the external interface" philosophy. It's a bit like saying that a car repair shop should only have tests to see if the wheels turn. Yes, ultimately I'm interested in the external behavior but I like my own, private, internal tests to be a bit more specific and to the point. Yes, if I refactor, I may have to change some of the tests, but unless it's a massive refactor, I'll only have to change a few and the fact that the other (unchanged) internal tests still work is a great indicator that the refactoring has been successful.

You can try to cover all internal cases using only the public interface and theoretically it's possible to test every internal method (or at least every one that matters) entirely by using the public interface but you may have to end up standing on your head to achieve this and the connection between the test cases being run through the public interface and the internal portion of the solution they're designed to test may be difficult or impossible to discern. Having pointed, individual tests that guarantee that the internal machinery is working properly is well worth the minor test changes that come about with refactoring - at least that's been my experience. If you have to make huge changes to your tests for every refactoring, then maybe this doesn't make sense, but in that case, maybe you ought to rethink your design entirely. A good design should be flexible enough to allow for most changes without massive redesigns.

这里有一篇关于私有方法单元测试的好文章。但是我不知道哪个更好,是让你的应用程序专门为测试而设计(就像只为测试而创建测试)还是使用反射来测试。 我敢肯定大多数人都会选择第二种方式。

如果您想对私有方法进行单元测试,那么可能会出现一些错误。单元测试(一般来说)用于测试类的接口,即类的公共(和受保护)方法。你当然可以“破解”一个解决方案(即使只是通过公开方法),但你可能还想考虑:

如果您想测试的方法确实值得测试,那么将它移到自己的类中可能是值得的。 向调用私有方法的公共方法添加更多测试,测试私有方法的功能。(正如评论者所指出的,只有当这些私有方法的功能确实是公共接口的一部分时,才应该这样做。如果它们实际上执行对用户隐藏的功能(即单元测试),这可能是不好的)。

在c#中,您可以使用我下面提供的代码。尽管我认为只有在绝对需要的时候才应该进行私有方法的单元测试。我遇到过一些我觉得有必要这样做的案例。下面是我在UnitTestBase类中创建的一些c#方法,我继承了我的UnitTest类(你也可以把它放在一个静态的“助手”类中)。HTH

protected internal static TResult? InvokePrivateInstanceMethod<TType, TResult>(string methodName, object?[]? methodArguments = null, params object?[]? constructorArguments)
{
    var classType = typeof(TType);
    var instance = Activator.CreateInstance(classType, constructorArguments);
    var privateMethodInfo = classType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
                                        .FirstOrDefault(m => m.IsPrivate &&
                                            m.Name.Equals(methodName, StringComparison.Ordinal) &&
                                            m.ReturnType.Equals(typeof(TResult)));
 
    if (privateMethodInfo is null)
    {
        throw new MissingMethodException(classType.FullName, methodName);
    }

    var methodResult = privateMethodInfo.Invoke(instance, methodArguments);
    if (methodResult is not null)
    {
        return (TResult)methodResult;
    }

    return default;
}

protected internal static async Task<TResult?> InvokePrivateInstanceMethodAsync<TType, TResult>(string methodName, object?[]? methodArguments = null, params object?[]? constructorArguments)
{
    var classType = typeof(TType);
    var instance = Activator.CreateInstance(classType, constructorArguments);
    var privateMethodInfo = classType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
                                        .FirstOrDefault(m => m.IsPrivate &&
                                            m.Name.Equals(methodName, StringComparison.Ordinal) &&
                                            m.ReturnType.Equals(typeof(Task<TResult>)));
            
    if (privateMethodInfo is null)
    {
        throw new MissingMethodException(classType.FullName, methodName);
    }

    var methodResult = privateMethodInfo.Invoke(instance, methodArguments);
    if (methodResult is not null)
    {
        return await (Task<TResult>)methodResult;
    }

    return default;
}

有时,测试私有声明是很好的。 基本上,编译器只有一个公共方法:Compile(string outputFileName, params string[] sourceSFileNames)。我相信您可以理解,如果不测试每个“隐藏”声明,就很难测试这样的方法!

这就是为什么我们创建了Visual t#:来简化测试。它是一个免费的。net编程语言(兼容c# v2.0)。

我们增加了‘。——“操作符。它就像'。操作符,除了您还可以从测试中访问任何隐藏的声明,而无需更改已测试项目中的任何内容。

看看我们的网站:免费下载。