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,因此列表添加失败。

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


当前回答

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

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

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

然而……

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

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

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

其他回答

您可以使用嵌套类来测试私有方法。例如(使用NUnit v3):

    
    internal static class A
    {
        // ... other code

        private static Int32 Sum(Int32 a, Int32 b) => a + b;

        [TestFixture]
        private static class UnitTests
        {
            [Test]
            public static void OnePlusTwoEqualsThree()
            {
                Assert.AreEqual(3, Sum(1, 2));
            }
        }
    }

此外,可以使用“部分类”特性将测试相关代码移动到另一个文件中,使用“条件编译”将其排除在发布版本之外,等等。先进的例子:

文件交流

    
    internal static partial class A
    {
        // ... other code

        private static Int32 Sum(Int32 a, Int32 b) => a + b;
    }

文件A.UnitTests.cs


#if UNIT_TESTING
    partial class A
    {
        [TestFixture]
        private static class UnitTests
        {
            [Test]
            public static void OnePlusTwoEqualsThree()
            {
                Assert.AreEqual(3, Sum(1, 2));
            }
        }
    }
#endif

测试私有方法的一种方法是通过反射。这也适用于NUnit和XUnit:

MyObject objUnderTest = new MyObject();
MethodInfo methodInfo = typeof(MyObject).GetMethod("SomePrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
object[] parameters = {"parameters here"};
methodInfo.Invoke(objUnderTest, parameters);

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

提取私有方法到另一个类,在该类上进行测试;阅读更多关于SRP原则(单一责任原则)

看起来你需要将私有方法提取到另一个类;在这方面应该是公开的。您应该测试另一个类的公共方法,而不是试图测试私有方法。

我们有以下场景:

Class A
+ outputFile: Stream
- _someLogic(arg1, arg2) 

我们需要测试_someLogic;但A类似乎扮演了过多的角色(违反SRP原则);只需将其重构为两个类

Class A1
    + A1(logicHandler: A2) # take A2 for handle logic
    + outputFile: Stream
Class A2
    + someLogic(arg1, arg2) 

这样就可以在A2上测试一些逻辑;在A1中,只需创建一些伪A2,然后注入到构造函数中,以测试A2被调用到名为someLogic的函数中。

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也不懂。

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

第一:大家新年快乐!

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

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