Visual Studio允许通过自动生成的访问器类对私有方法进行单元测试。我已经编写了一个私有方法的测试,它编译成功,但在运行时失败。一个相当小的版本的代码和测试是:

//in project MyProj
class TypeA
{
    private List<TypeB> myList = new List<TypeB>();

    private class TypeB
    {
        public TypeB()
        {
        }
    }

    public TypeA()
    {
    }

    private void MyFunc()
    {
        //processing of myList that changes state of instance
    }
}    

//in project TestMyProj           
public void MyFuncTest()
{
    TypeA_Accessor target = new TypeA_Accessor();
    //following line is the one that throws exception
    target.myList.Add(new TypeA_Accessor.TypeB());
    target.MyFunc();

    //check changed state of target
}

运行时错误为:

Object of type System.Collections.Generic.List`1[MyProj.TypeA.TypeA_Accessor+TypeB]' cannot be converted to type 'System.Collections.Generic.List`1[MyProj.TypeA.TypeA+TypeB]'.

根据智能感知-因此我猜编译器-目标类型是TypeA_Accessor。但是在运行时它的类型是TypeA,因此列表添加失败。

有什么方法可以停止这个错误吗?或者,更有可能的是,其他人有什么其他的建议(我预测可能是“不要测试私有方法”和“不要使用单元测试来操纵对象的状态”)。


当前回答

Ermh... Came along here with exactly the same problem: Test a simple, but pivotal private method. After reading this thread, it appears to be like "I want to drill this simple hole in this simple piece of metal, and I want to make sure the quality meets the specs", and then comes "Okay, this is not to easy. First of all, there is no proper tool to do so, but you could build a gravitational-wave observatory in your garden. Read my article at http://foobar.brigther-than-einstein.org/ First, of course, you have to attend some advanced quantum physics courses, then you need tons of ultra-cool nitrogenium, and then, of course, my book available at Amazon"...

换句话说……

不,重要的事先做。

每一个方法,可能是私有的,内部的,受保护的,公共的都必须是可测试的。必须有一种方法可以毫不费力地实现这样的测试,就像这里介绍的那样。

为什么?正是因为到目前为止一些贡献者所做的架构介绍。也许简单地重申一下软件原则就可以消除一些误解。

在这种情况下,通常的怀疑对象是:OCP、SRP和KIS。

But wait a minute. The idea of making everything publicly available is more of less political and a kind of an attitude. But. When it comes to code, even in then Open Source Community, this is no dogma. Instead, "hiding" something is good practice to make it easier to come familiar with a certain API. You would hide, for example, the very core calculations of your new-to-market digital thermometer building block--not to hide the maths behind the real measured curve to curious code readers, but to prevent your code from becoming dependent on some, perhaps suddenly important users who could not resist using your formerly private, internal, protected code to implement their own ideas.

我在说什么?

private double translatemmeasurementintolinear (double actualMeasurement);

现在很容易宣布水瓶座时代或被称为什么,但如果我的传感器从1.0到2.0,翻译的实现……可能会从一个简单的易于理解和对每个人都“可重复使用”的线性方程,变成一个相当复杂的计算,使用分析或其他东西,所以我会破坏别人的代码。为什么?因为他们不懂软件编码的基本原理,甚至KIS也不懂。

简而言之:我们需要一种简单的方法来测试私有方法——毫不费力。

第一:大家新年快乐!

第二:预习你的建筑师课程。

第三:“公共”修饰语指的是宗教,而不是解决方案。

其他回答

另一个没有提到的选项是创建单元测试类作为您正在测试的对象的子类。NUnit的例子:

[TestFixture]
public class UnitTests : ObjectWithPrivateMethods
{
    [Test]
    public void TestSomeProtectedMethod()
    {
        Assert.IsTrue(this.SomeProtectedMethod() == true, "Failed test, result false");
    }
}

这将允许轻松测试私有和受保护的(但不继承私有)方法,并且允许将所有测试与实际代码分开,这样就不必将测试程序集部署到生产环境中。在许多继承对象中,将私有方法切换为受保护的方法是可以接受的,而且这是一个相当简单的更改。

然而……

虽然这是解决如何测试隐藏方法问题的一种有趣的方法,但我不确定我是否会主张这是在所有情况下解决问题的正确解决方案。在内部测试一个对象似乎有点奇怪,我怀疑在某些情况下,这种方法可能会让您失败。(例如,不可变对象可能会使一些测试非常困难)。

虽然我提到了这种方法,但我认为这更像是一个头脑风暴的建议,而不是一个合理的解决方案。对它持保留态度。

编辑:我发现人们投票否决这个答案真的很滑稽,因为我明确地把它描述为一个坏主意。这是否意味着人们同意我的观点?我很困惑.....

“没有所谓的标准或最佳实践,可能它们只是流行的观点。”

同样的道理也适用于这个讨论。

这取决于你认为什么是单元,如果你认为unit是一个类,那么你只会碰到公共方法。如果你认为UNIT是代码行,敲打私有方法不会让你感到内疚。

如果你想调用私有方法,你可以使用“PrivateObject”类并调用调用方法。你可以观看这个youtube视频(http://www.youtube.com/watch?v=Vq6Gcs9LrPQ),它展示了如何使用“PrivateObject”,还讨论了私有方法的测试是否合乎逻辑。

我使用这个helper(对象类型扩展)

 public static  TReturn CallPrivateMethod<TReturn>(
        this object instance,
        string methodName,
        params object[] parameters)
    {
        Type type = instance.GetType();
        BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Instance;
        MethodInfo method = type.GetMethod(methodName, bindingAttr);

        return (TReturn)method.Invoke(instance, parameters);
    }

你可以这样叫它

Calculator systemUnderTest = new Calculator();
int result = systemUnderTest.CallPrivateMethod<int>("PrivateAdd",1,8);

优点之一是它使用泛型来预先确定返回类型。

在VS 2005/2008中,你可以使用私有访问器来测试私有成员,但是这种方法在后来的VS版本中消失了

你可以使用PrivateObject类:

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(expectedVal, retVal);

注意:PrivateObject和PrivateType不适用于针对netcoreapp2.0 - GitHub Issue 366的项目