C# C语言中的单元测试私有方法#
VisualStudio允许通过自动生成的访问器类对私有方法进行单元测试。我编写了一个私有方法的测试,该方法编译成功,但在运行时失败。代码和测试的最低版本为:C# C语言中的单元测试私有方法#,c#,unit-testing,mstest,C#,Unit Testing,Mstest,VisualStudio允许通过自动生成的访问器类对私有方法进行单元测试。我编写了一个私有方法的测试,该方法编译成功,但在运行时失败。代码和测试的最低版本为: //in project MyProj class TypeA { private List<TypeB> myList = new List<TypeB>(); private class TypeB { public TypeB() {
//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
}
根据intellisense,因此我猜编译器目标是类型A_访问器。但在运行时,它的类型是TypeA,因此列表添加失败
有什么方法可以阻止这个错误吗?或者,更可能的是,其他人还有什么建议(我预测可能是“不要测试私有方法”和“不要让单元测试操纵对象的状态”)。是的,不要单元测试私有方法。。。。单元测试的思想是通过其公共“API”来测试单元 如果您发现需要测试大量的私有行为,那么很可能您有一个新的“类”隐藏在您尝试测试的类中,提取它并通过它的公共接口进行测试 一条建议/思考工具。。。。。有一种观点认为,任何方法都不应该是私有的。这意味着所有方法都应该存在于对象的公共接口上。。。。如果你觉得需要将其保密,它很可能存在于另一个对象上
这条建议在实践中不太管用,但它大多是好建议,通常会促使人们将其对象分解为更小的对象。这里的另一个想法是将测试扩展到“内部”类/方法,使这种测试更具白盒感。您可以在程序集上使用InternalsVisibleToAttribute将这些属性公开给单独的单元测试模块 结合密封类,您可以实现这样的封装,即测试方法只能从您的方法的UnitTestAssembly中看到。考虑到密封类中的受保护方法实际上是私有的。< /P>
[assembly: InternalsVisibleTo("MyCode.UnitTests")]
namespace MyCode.MyWatch
{
#pragma warning disable CS0628 //invalid because of InternalsVisibleTo
public sealed class MyWatch
{
Func<DateTime> _getNow = delegate () { return DateTime.Now; };
//construktor for testing purposes where you "can change DateTime.Now"
internal protected MyWatch(Func<DateTime> getNow)
{
_getNow = getNow;
}
public MyWatch()
{
}
}
}
[汇编:InternalsVisibleTo(“MyCode.UnitTests”)]
名称空间MyCode.MyWatch
{
#pragma warning disable CS0628//由于内部可视性而无效
公共密封级MyWatch
{
Func_getNow=delegate(){return DateTime.Now;};
//用于测试目的的构造函数,其中“可以立即更改日期时间”
内部保护MyWatch(Func getNow)
{
_getNow=getNow;
}
公共MyWatch()
{
}
}
}
和单元测试:
namespace MyCode.UnitTests
{
[TestMethod]
public void TestminuteChanged()
{
//watch for traviling in time
DateTime baseTime = DateTime.Now;
DateTime nowforTesting = baseTime;
Func<DateTime> _getNowForTesting = delegate () { return nowforTesting; };
MyWatch myWatch= new MyWatch(_getNowForTesting );
nowforTesting = baseTime.AddMinute(1); //skip minute
//TODO check myWatch
}
[TestMethod]
public void TestStabilityOnFebruary29()
{
Func<DateTime> _getNowForTesting = delegate () { return new DateTime(2024, 2, 29); };
MyWatch myWatch= new MyWatch(_getNowForTesting );
//component does not crash in overlap year
}
}
namespace MyCode.UnitTests
{
[测试方法]
public void TestminuteChanged()
{
//及时观察交通情况
DateTime baseTime=DateTime.Now;
DateTime nowforTesting=基准时间;
Func_getNowForTesting=delegate(){return nowforTesting;};
MyWatch MyWatch=新的MyWatch(\u GetNow用于测试);
nowforTesting=baseTime.AddMinute(1);//跳过分钟
//我要检查我的手表
}
[测试方法]
2月29日公开作废测试稳定性()
{
Func_getNowForTesting=delegate(){返回新的日期时间(2024,2,29);};
MyWatch MyWatch=新的MyWatch(\u GetNow用于测试);
//组件不会在重叠年份崩溃
}
}
您可以使用该类:
Class目标=新类();
PrivateObject obj=新的PrivateObject(目标);
var retVal=obj.Invoke(“PrivateMethod”);
Assert.AreEqual(expectedVal、retVal);
注:PrivateObject
和PrivateType
不适用于以netcoreapp2.0为目标的项目-“没有所谓的标准或最佳实践,它们可能只是流行意见”。
这一讨论也是如此
这完全取决于你认为什么是单元,如果你认为单元是一个类,那么你只会点击公共方法。若你们认为单位是代码行,那个么打击私有方法不会让你们感到内疚
如果要调用私有方法,可以使用“PrivateObject”类并调用invoke方法。您可以观看这段深入的youtube视频(),它展示了如何使用“PrivateObject”,还讨论了私有方法的测试是否符合逻辑。在VS2005/2008中,您可以使用它来测试私有成员,但在VS的更高版本中,这种方法是消失的测试私有方法的一种方法是通过反射。这也适用于NUnit和XUnit:
MyObject objUnderTest = new MyObject();
MethodInfo methodInfo = typeof(MyObject).GetMethod("SomePrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
object[] parameters = {"parameters here"};
methodInfo.Invoke(objUnderTest, parameters);
将私有方法提取到另一个类,对该类进行测试;了解有关SRP原则(单一责任原则)的更多信息 似乎需要将
private
方法提取到另一个类;在这应该是public
。您应该测试另一个类的public
方法,而不是尝试测试private
方法
我们有以下情况:
Class A
+ outputFile: Stream
- _someLogic(arg1, arg2)
我们需要测试\u someLogic
的逻辑;但似乎A类
的作用超出了其需要(违反了SRP原则);只需重构成两个类
Class A1
+ A1(logicHandler: A2) # take A2 for handle logic
+ outputFile: Stream
Class A2
+ someLogic(arg1, arg2)
这样就可以在A2上测试
someLogic
;在A1中,只需创建一些伪A2,然后将其注入构造函数,以测试A2是否被调用到名为someLogic
Ermh的函数。。。这里出现了完全相同的问题:测试一个简单但关键的私有方法。读过这条线后,它看起来像是“我想在这片简单的金属上钻这个简单的孔,我想确保质量符合规格”,然后就来了“好吧,这可不容易。首先,目前还没有合适的工具,但你可以在花园里建立一个引力波观测站。首先阅读我的文章,当然,你必须参加一些高级量子物理课程,然后你需要大量的超冷氮,然后,当然,我的书可以在亚马逊上买到“
换句话说
不,首先要做的事
每一种方法,可能都是私有的、内部的、受保护的、公共的都是可测试的。必须有一种方法来实现这些测试,而不需要像这里介绍的那样费事
为什么?正是因为建筑师
Class A1
+ A1(logicHandler: A2) # take A2 for handle logic
+ outputFile: Stream
Class A2
+ someLogic(arg1, arg2)
[TestFixture]
public class UnitTests : ObjectWithPrivateMethods
{
[Test]
public void TestSomeProtectedMethod()
{
Assert.IsTrue(this.SomeProtectedMethod() == true, "Failed test, result false");
}
}
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);