jun*_*iro 265 c# unit-testing
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
}
Run Code Online (Sandbox Code Playgroud)
运行时错误是:
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]'.
Run Code Online (Sandbox Code Playgroud)
根据intellisense - 因此我猜编译器 - 目标是TypeA_Accessor类型.但在运行时它的类型为TypeA,因此列表添加失败.
有什么办法可以阻止这个错误吗?或者,或许更有可能的是,其他人有什么其他建议(我预测可能"不测试私有方法"和"没有单元测试操纵对象的状态").
Scu*_*tle 612
您可以使用PrivateObject类
Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(expectedVal, retVal);
Run Code Online (Sandbox Code Playgroud)
Kei*_*las 266
是的,不要测试私有方法....单元测试的想法是通过其公共'API'测试单元.
如果您发现需要测试很多私有行为,那么很可能您在要测试的类中隐藏了一个新的"类",将其解压缩并通过其公共接口进行测试.
一条建议/思考工具.....有一种想法,任何方法都不应该是私密的.意味着所有方法都应该存在于对象的公共接口上....如果您觉得需要将其设置为私有,则很可能存在于另一个对象上.
这条建议在实践中并没有很好的解决,但它的建议大多是好的,而且往往会促使人们将对象分解成更小的对象.
Shi*_*ala 91
同样适用于此讨论.

这一切都取决于你认为是一个单位,如果你认为UNIT是一个类,那么你只会点击公共方法.如果你认为UNIT是代码行,打私人方法不会让你感到内疚.
如果要调用私有方法,可以使用"PrivateObject"类并调用invoke方法.您可以观看此深入视频(http://www.youtube.com/watch?v=Vq6Gcs9LrPQ),其中显示了如何使用"PrivateObject",还讨论了私有方法的测试是否合乎逻辑.
Jef*_*eff 47
这里的另一个想法是将测试扩展到"内部"类/方法,给出更多的白盒感测.您可以在程序集上使用InternalsVisibleToAttribute将这些内容公开给单独的单元测试模块.
结合密封类,您可以进行这样的封装,即测试方法只能从您的方法的unittest程序集中看到.考虑密封类中的受保护方法事实上是私有的.
[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()
{
}
}
}
Run Code Online (Sandbox Code Playgroud)
和单元测试:
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
}
}
Run Code Online (Sandbox Code Playgroud)
Jac*_*son 25
测试私有方法的一种方法是通过反射.这也适用于NUnit和XUnit:
MyObject objUnderTest = new MyObject();
MethodInfo methodInfo = typeof(MyObject).GetMethod("SomePrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
object[] parameters = {"parameters here"};
methodInfo.Invoke(objUnderTest, parameters);
Run Code Online (Sandbox Code Playgroud)
Rog*_*ill 11
另一个未提及的选项是将单元测试类创建为您正在测试的对象的子类。NUnit 示例:
[TestFixture]
public class UnitTests : ObjectWithPrivateMethods
{
[Test]
public void TestSomeProtectedMethod()
{
Assert.IsTrue(this.SomeProtectedMethod() == true, "Failed test, result false");
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许轻松测试私有和受保护(但不是继承的私有)方法,并且它允许您将所有测试与实际代码分开,这样您就不会将测试程序集部署到生产中。在许多继承的对象中,将私有方法切换为受保护的方法是可以接受的,这是一个非常简单的更改。
然而...
虽然这是解决如何测试隐藏方法的问题的有趣方法,但我不确定我是否会主张这在所有情况下都是问题的正确解决方案。在内部测试一个对象似乎有点奇怪,我怀疑在某些情况下这种方法可能会让你失望。(例如,不可变对象可能会使某些测试变得非常困难)。
虽然我提到了这种方法,但我认为这更像是一个集思广益的建议,而不是一个合法的解决方案。把它和一粒盐一起吃。
编辑:我发现人们对这个答案投反对票真的很有趣,因为我明确地将其描述为一个坏主意。这是否意味着人们同意我的观点?我感到很困惑.....
小智 10
Ermh ...出现了完全相同的问题:测试一个简单但关键的私有方法。读完该线程后,看起来就像“我想在这种简单的金属上钻出这个简单的孔,然后确保质量符合规格”,然后出现“好吧,这并不容易。首先,有这样做没有适当的工具,但你可以建立在你的花园引力波天文台。在阅读我的文章http://foobar.brigther-than-einstein.org/首先,当然,你必须参加一些高级量子物理学课程,然后您需要大量的超冷氮,然后,当然,我的书可以在亚马逊上找到。”
换一种说法...
不,第一件事。
每种方法(可能是私有的,内部的,受保护的,公开的)都必须是可测试的。必须有一种方法来实现这种测试,而无需如本文所述。
为什么?正是由于一些贡献者到目前为止所做的体系结构提及。简单地重申软件原理可能会消除一些误解。
在这种情况下,通常的怀疑者是:OCP,SRP和一如既往的KIS。
等一下 使所有内容公开可用的想法更多是出于政治目的,而不是一种态度。但。关于代码,即使在当时的开源社区中,这也不是教条。相反,“隐藏”某些东西是使您更容易熟悉某个API的好习惯。例如,您将隐藏新上市的数字温度计构建模块的核心计算,而不是将数学运算隐藏在真实测得的曲线后面,以免引起好奇的代码阅读者,但要防止您的代码变得依赖某些代码,也许突然之间重要的用户无法抗拒使用您以前私有的,内部受保护的代码来实现自己的想法。
我在说什么
私人double TranslateMeasurementIntoLinear(double actualMeasurement);
宣称水瓶座时代或现在称为水瓶座很容易,但是如果我的传感器从1.0升级到2.0,则Translate ...的实现可能会从一个易于理解的简单线性方程式改变为“适用于所有人”,到使用分析或其他方法进行相当复杂的计算,因此我会破坏其他人的代码。为什么?因为他们不了解软件编码的基本原理,甚至都不了解KIS。
简而言之:我们需要一种简单的方法来测试私有方法-事不宜迟。
第一:大家新年快乐!
第二:演练您的架构师课程。
第三:“公共”修饰语是宗教,而不是解决方案。
来自《有效处理遗留代码》一书:
“如果我们需要测试一个私有方法,我们应该将其公开。如果公开它让我们感到困扰,在大多数情况下,这意味着我们的类做得太多了,我们应该修复它。”
根据作者的说法,修复它的方法是创建一个新类并将方法添加为public.
作者进一步解释道:
“好的设计是可测试的,不可测试的设计就是糟糕的。”
public因此,在这些限制内,您唯一真正的选择是在当前类或新类中创建方法。
虽然这并不能真正回答问题,但我现在的首选方法是在同一个 C# 项目中并置代码并进行测试,并使用<ClassName>.Tests.cs. 然后我使用internal访问修饰符而不是private.
在项目文件中,我有这样的内容:
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<Compile Remove="**\*.Tests.cs" />
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)
在构建中排除测试文件release。根据需要修改。
常见问题解答 1:但有时您还想在发布(优化)版本中测试代码。
答:我觉得没有必要。我相信编译器会完成它的工作而不会扰乱我的意图。到目前为止,我没有理由质疑它的能力。
FAQ 2:但我真的想保留该方法(或类)private。
回答:本页中有很多优秀的解决方案可供尝试。根据我的经验,将访问修饰符设置为internal通常就足够了,因为方法(或类)在其定义的项目之外不可见。除此之外,没有什么可隐藏的了。
我使用这个助手(对象类型扩展)
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);
}
Run Code Online (Sandbox Code Playgroud)
你可以这样称呼它
Calculator systemUnderTest = new Calculator();
int result = systemUnderTest.CallPrivateMethod<int>("PrivateAdd",1,8);
Run Code Online (Sandbox Code Playgroud)
优点之一是它使用泛型来预先确定返回类型。
| 归档时间: |
|
| 查看次数: |
193265 次 |
| 最近记录: |