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,因此列表添加失败。
有什么方法可以停止这个错误吗?或者,更有可能的是,其他人有什么其他的建议(我预测可能是“不要测试私有方法”和“不要使用单元测试来操纵对象的状态”)。
现在是2022年了!
...我们有。net 6
虽然这并没有真正地回答问题,但我现在更喜欢的方法是在同一个c#项目中搭配代码和测试,使用<ClassName>. tests .cs这样的命名约定。然后我使用内部访问修饰符而不是私有。
在项目文件中,我有这样的东西:
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<Compile Remove="**\*.Tests.cs" />
</ItemGroup>
在发布版本中排除测试文件。根据需要进行修改。
FAQ 1:但是有时候你也想在发布(优化)版本中测试代码。
答:我觉得没必要。我相信编译器将完成它的工作而不会打乱我的意图。到目前为止,我还没有理由质疑它这样做的能力。
FAQ 2:但是我真的想保持方法(或类)私有。
答:本页有许多优秀的解决方案可供尝试。根据我的经验,将访问修饰符设置为内部通常就足够了,因为方法(或类)在它所定义的项目之外是不可见的。除此之外,没什么好隐瞒的了。
另一个没有提到的选项是创建单元测试类作为您正在测试的对象的子类。NUnit的例子:
[TestFixture]
public class UnitTests : ObjectWithPrivateMethods
{
[Test]
public void TestSomeProtectedMethod()
{
Assert.IsTrue(this.SomeProtectedMethod() == true, "Failed test, result false");
}
}
这将允许轻松测试私有和受保护的(但不继承私有)方法,并且允许将所有测试与实际代码分开,这样就不必将测试程序集部署到生产环境中。在许多继承对象中,将私有方法切换为受保护的方法是可以接受的,而且这是一个相当简单的更改。
然而……
虽然这是解决如何测试隐藏方法问题的一种有趣的方法,但我不确定我是否会主张这是在所有情况下解决问题的正确解决方案。在内部测试一个对象似乎有点奇怪,我怀疑在某些情况下,这种方法可能会让您失败。(例如,不可变对象可能会使一些测试非常困难)。
虽然我提到了这种方法,但我认为这更像是一个头脑风暴的建议,而不是一个合理的解决方案。对它持保留态度。
编辑:我发现人们投票否决这个答案真的很滑稽,因为我明确地把它描述为一个坏主意。这是否意味着人们同意我的观点?我很困惑.....
如果PrivateObject不可用,并且被测试的类不是密封类,则可以将想要公开的方法和属性设置为受保护的。在单元测试文件中创建一个带有内部方法的继承类,这些内部方法公开了测试中的私有方法/属性。
如果被测试的类是:
class MyClass{private string GetStr(string x, int y) => $"Success! {x} {y}";}
改为:
class MyClass{protected string GetStr(string x, int y) => $"Success! {x} {y}";}
在单元测试文件中创建一个继承类,如下所示:
class MyClassExposed: MyClass
{
internal string ExposedGetStr(string x, int y)
{
return base.GetStr(x, y);
}
}
现在您可以使用继承的类MyClassExposed来测试公开的方法和属性。