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