Visual Studio允许通过自动生成的访问器类对私有方法进行单元测试。我已经编写了一个成功编译私有方法的测试,但它在运行时失败。代码的最低版本和测试是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| //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
} |
运行时错误为:
1
| 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]'. |
根据intellisense,因此我猜编译器目标是typea_访问器类型。但在运行时,它是typea类型,因此列表添加失败。
有什么方法可以阻止这个错误吗?或者,更可能的是,其他人有什么其他建议(我预测可能"不测试私有方法"和"不让单元测试操纵对象的状态")。
- 对于私有类typeb,您需要一个访问器。访问器typea_访问器提供对typea的私有和受保护方法的访问。但是,typeb不是一种方法。这是一个班。
- 访问器提供对私有/受保护方法、成员、属性和事件的访问。它不提供对类内私有/受保护类的访问。私有/受保护类(typeb)仅用于拥有类(typea)的方法。所以基本上你是想把私有类(typeb)从typea之外添加到私有的"mylist"中。因为您使用的是访问器,所以访问mylist没有问题。但是不能通过访问器使用typeb。可能的解决方案是将类型B移出类型A。但它会破坏你的设计。
- 认为测试私有方法应该通过以下stackoverflow.com/questions/250692/&hellip;
可以使用privateobject类
1 2 3 4
| Class target = new Class();
PrivateObject obj = new PrivateObject (target );
var retVal = obj .Invoke("PrivateMethod");
Assert .AreEqual(expectedVal, retVal ); |
- 这是正确的答案,因为微软已经添加了privateobject。
- 回答不错,但请注意,privatemethod需要"保护"而不是"私有"。
- 在抽象类中调用方法如何?(不能单独构造的类…)。实例化派生类?嗯,不太好。其他一些反射技巧?任何人…?
- @赫尔曼:也许我误解了你,但如果你是在暗示隐私对象只能访问受保护的成员而不是私人成员,那你就错了。
- @我认为私人类型的课程会有帮助。有关详细信息,请参阅stackoverflow.com/questions/4883944/&hellip;
- 如果私有方法是无效的呢?
- @拉法尔·科辛斯基那么你希望它做什么?如果它是空的,那么它可以成功运行或抛出异常。调用obj.invoke("privatemethod")时,可以声明异常
- @Herbalmart-现在看来私密物品可以和私人方法一起使用(不仅仅是保护)。我只是用了一种私人方法,而且效果很好。这是一个非常有用的功能!
- 这个建议对静态类有效吗?如果是,你能举个例子吗?
- 如何解决方法名重构的问题?
- @对于静态方法,可以使用"privatetype pt=new privatetype(typeof(myclass));",然后对pt对象调用invoke static,就像对私有对象调用invoke一样。
- 这个很有用。在5分钟内启动并运行,让我相信我认为正确的东西应该是私有的,也可以进行单元测试。
- 如果有人想知道从mstest.testframework v1.2.1开始的情况-privateobject和privatetype类对于面向.NET核心2.0的项目不可用-这有一个github问题:github.com/microsoft/testfx/issue s/366
- call method with parameters的私有对象?
- 调用静态method with parameters的privateobject?
- @kiquenet obj.invoke("testmethod",topassin);该方法将对象数组作为参数
- 如果"privatemethod"抛出异常如何处理?
- obj.invoke("privatemethod")允许使用第二个参数,即表示方法参数的对象数组,例如obj.invoke("privatemethod",myparams),其中myparams是对象[]
是的,不要测试私有方法….单元测试的思想是通过它的公共"api"来测试单元。
如果你发现你需要测试很多私人行为,很可能你有一个新的"类"隐藏在你试图测试的类中,提取它并通过它的公共接口测试它。
一条建议/思考工具……有一种观点认为任何方法都不应该是私有的。这意味着所有方法都应该位于对象的公共接口上….如果你觉得你需要让它私有化,它很可能生活在另一个物体上。
这条建议在实践中并不十分有效,但它大多是好的建议,而且常常会促使人们将对象分解成更小的对象。
- 我不同意。在ood中,私有方法和属性是一种不重复自己的固有方式(en.wikipedia.org/wiki/don%27t_repeat_yourself)。黑盒编程和封装背后的思想是向订户隐藏技术细节。因此,在代码中确实需要有非常重要的私有方法和属性。如果它不是琐碎的,就需要进行测试。
- 它不是内在的,一些OO语言没有私有方法,私有属性可以包含带有公共接口的对象,这些公共接口可以测试。
- 这个建议的要点是,如果你的对象做了一件事,而且是干的,那么通常没有什么理由使用私有方法。通常,私有方法会做一些对象并不真正负责但非常有用的事情,如果不是琐碎的话,那么它通常是另一个对象,因为它可能会违反SRP。
- 错了。您可能希望使用私有方法来避免代码重复。或用于验证。或者为了公共世界不应该知道的许多其他目的。
- 不。。。。。你会发现,这些方法需要生活在其他地方,否则你会违反SRP
- "不要测试私有方法"-这是一个相当大的声明,我真的不确定我能同意。你有微软的文档来支持这个吗?
- 微软不是真相的来源,我不知道他们是否有任何关于这个问题的想法:)在微软加入单元测试的想法之前,看看这些想法是否存在!但是,如何处理私有方法的概念在早期的极端编程用户组中被那些将单元测试推广到世界各地的人热烈讨论。这个想法是积极的重构,和SRP,不应该有太多的私人需求,如果有,它应该很容易通过公共接口测试。
- 当你被甩在一个如此可怕的OO代码库上,并要求"逐步改进它"时,我很失望地发现我无法对私有方法进行一些最初的测试。是的,也许在教科书中,这些方法不会出现在这里,但在现实世界中,我们的用户有产品需求。我不能不在项目中进行一些测试就进行大规模的"查看MA干净代码"重构。感觉就像是另一个例子,强迫练习程序员走一些看似简单的好方法,但却没有考虑到真正的乱七八糟的东西。
- 这是一个由类似TDD的东西驱动的设计过程,当你处理一个现有的代码库时,如果它很糟糕,那么你使用不同的技术,尽管我仍然不测试私有的方法,但是有一本好书"有效地处理遗留代码"(我是其中的一个贡献者),它处理这些问题的准确方式是@dune.rocks。
- @如果我试图写干净的代码,这意味着我将把一个大方法的一部分提取成较小的方法,并从最初的大方法中调用它们。把这些较小的新方法保持在同一类和私有中是有意义的,不是吗?通常,这些较小的方法可以包含一些应该测试的非平凡逻辑,并且可以多次重复使用。您的意思是说这个逻辑应该只通过公共的最初大型方法进行测试吗?
- @乔登戴尔大方法分解成更小的方法,很好的第一步。下一步是将对象拆分为更小的基于SRP的对象。你的对象只会通过它的公共接口(被测试的单元)使用,所以要通过这个测试它。不能通过公共接口轻松测试的重要隐藏逻辑通常是另一个对象。我从1999年开始进行极限编程和单元测试,从来没有测试过私有方法。我见过有人这样做,我总能用组合对象将他们的代码重构成更简单的OO代码。
- @基思尼古拉斯当然,我同意这一点,在纯OOP的情况下。然而,例如,在Web服务中,即使语言是C,也不一定要执行纯OOP。在我当前的项目中,我有一个接受请求的服务,处理请求的代码基本上是过程性的,主要使用DTO,当然还有许多框架对象,但这仅仅是因为对象是主要的语言结构。一个类接受请求,处理请求的大多数代码都在一个处理程序类中。将处理程序划分为具有公共接口IMO的多个对象真的没有意义
- @Joedyndale Dunno你的代码是什么样子的。我做了很多Web API的工作,从来没有对私有方法(或者至少没有一种方法不容易通过公共接口测试)。如果你愿意的话,可以安排一个聊天时间看看代码吗?
- @基思·尼古拉斯也许我只是在想用另一种(可能更糟的)方式来测试。我的方法也可以通过公共接口进行测试,但是程序必须为每个测试做很多不必要的工作,或者在一个测试中测试太多的东西。我可以将提取的方法分离到具有公共接口的单独类中,但这种分离方式并不比简单地从现有类中的方法开始公开更有意义。后者是可行的,但严格地说,国际海事组织是不正确的。
- "是的,不要测试私有方法….单元测试的思想是通过它的公共"api"来测试单元。-这是完全错误的。如果您的单元(让我们来讨论一个简单的类)有自己的内部需求,并且满足了一些方法,那么我应该能够单独测试它们,看看是否满足需求。举个例子:这个类在外部看起来工作得很好,但是在内部它不做一些FIFO的工作,这会提高性能。好的,代码可以工作,但我希望它可以这样工作,这是最初预期的。
- @纽豪斯,我要看一些代码,我敢打赌,你在一个类中有太多的功能,可以拆分……FIFO是一个独立的实体。我从1999年开始进行极限编程和单元测试,18年后,我还没有看到任何我认为需要对私有事物进行测试的代码,除了那些不好分解的代码,它们通常在分开时工作得更好。
- @KeithNicholas我不会在类中以任何方式包装助手方法,如果这取决于用户类的成员和真正的内部逻辑的话。因此,它应该是私有的,不能被其他人调用。但是我仍然希望对它进行单元测试,因为许多方法可能依赖于它。是的,我可以简单地对用户方法进行单元测试,但是如果我看到错误不是用户代码,而是通用的内部代码,那么它可能会加快调试速度。也许这有时是边缘的,但仍然存在。
- 真的。。。无休止的争论/批评您应该使用的软件取决于您所工作的团队和您正在开发的软件类型。水平快速一次性工具或垂直高度依赖库。两者在任何情况下都不起作用。并且针对这两种不同的情况设计了不同的语言。好的程序员知道在哪里使用。DR
- 假设我分解代码并将私有方法提取到一个可以从程序集/dll测试的类中。现在的问题是:如果我觉得不应该在公共接口中提供这个提取的类,如何隐藏它?对我来说,它与提供可用接口的目标相矛盾,因为现在程序集的客户机可以看到实现细节。
- @Jaro在类上使用关键字internal。这正是它的意义所在
- 这不应该是公认的答案。
- 虽然将大类分解为小类并将私有方法公开的建议听起来是正确的,但事实并非如此。您最终会得到许多小类,阅读并理解代码将是一场噩梦。
- @Mertakcakaya不是我的经验(已经编程40年了)。通常当人们说我发现他们有错误的抽象。
- 我不是完全靠这个卖的,这就是为什么。假设我有一个相当大的方法。大约300行在中频内,另外300行在中频内。我更喜欢的是类似于x ? PrivateMethod1() : PrivateMethod2();的东西,我不确定将这些方法引入新的类中是否是正确的解决方案,但我仍然希望将它们与公共方法分开测试。此外,创建一个新对象会创建一些公共接口,这些接口可能只存在于一个地方,因为它只属于这个类。
- @Vipero07如果你有一个包含300行和另外300行的if语句,那么我们甚至不会在同一个页面上讨论什么是好的设计。对我来说,这不应该出现在考虑周全的准则中。这意味着在600多行代码中可能有多个类。
- @KeithNicholas我不一定不同意,但是让我们假设您没有编写完整的代码库,并且希望在修复错误的代码位上进行单元测试。随着时间的推移,慢慢地迁移到一个考虑充分的代码库…它实际上不是重写所有内容的解决方案,尤其是在没有完全理解代码被修复的意图的情况下。当考虑开始时的弱因子分解时,是这样的。
- @Vipero07当然,如果你的论点是你从糟糕的代码和糟糕的设计开始,那么你可能需要做一些折衷的事情作为中间步骤。不过,我已经说过建议您可能使用refactoring.com/catalog/replaceMethodwithmethodObject.html。不过,我最初的建议是这样的,有了充分考虑的代码,您不需要测试私有方法,而且大多数情况下,您不需要任何私有方法。如果你觉得你确实需要一个,那么它是值得思考的原因和有一个更好的设计。
- @阿什顿人,我认为人们努力的是,他们想象他们目前的设计,可以看到所有这些问题与测试私人零件,这似乎是"错误的"。当你把它作为你设计的驱动力时,你会得到不同的设计。它不是让事物公开化,并把它们转移到新的类中,而是让它们易于组合。从表面上看,从你所说的,一方面你说你有一些东西可以在云中存储文件,另一方面,你有一个业务驱动的名称约定,对我来说,它们听起来像是不同的概念性东西。
"没有所谓的标准或最佳实践,可能它们只是流行的观点"。
这个讨论也是如此。
这完全取决于你认为单元是什么,如果你认为单元是一个类,那么你只会碰到公共方法。如果你认为单位是一行代码,打私人方法不会让你感到内疚。
如果要调用私有方法,可以使用"privateobject"类并调用invoke方法。你可以观看这段深入的YouTube视频(http://www.youtube.com/watch?v=vq6gcs9lrpq),说明如何使用"privateobject",并讨论私有方法的测试是否合乎逻辑。
这里的另一个想法是将测试扩展到"内部"类/方法,使测试有更多的白盒意义。可以在程序集上使用InternalsVisibleToAttribute将它们公开给单独的单元测试模块。
结合密封类,您可以接近这样的封装,这样测试方法只能从UnitTest程序集中看到您的方法。考虑到密封类中的受保护方法实际上是私有的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| [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()
{
}
}
} |
单元测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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
}
} |
- 我不确定我明白。InternalsVisibleToAttribute使标记为"内部"的方法和属性可访问,但我的字段和方法是"私有"的。你是建议我把事情从私人变成内部的吗?我想我误解了。
- 是的,这就是我的建议。这有点"黑客",但至少他们不是"公众"。
- 这是一个很好的答案,因为它没有说"不要测试私有方法",但是的,它相当"黑客"。我希望有解决办法。在我看来,说"私有方法不应该被测试"是不好的,因为它等同于"私有方法不应该是正确的"。
- 亚肯,我也被那些声称私有方法不应该在单元测试中测试的人搞糊涂了。公共API是输出,但有时错误的实现也会给出正确的输出。或者实现产生了一些不好的副作用,例如持有不必要的资源,引用对象阻止GC收集它等等,除非它们提供可以覆盖私有方法而不是单元测试的其他测试,否则我认为它们不能维护100%测试过的代码。
- 我同意马斯蒂克。这应该是公认的答案。
- 很好。我不知道。正是我需要的。假设我构建了一个供其他人使用的第三方组件,但我只想公开一个非常有限的API。在内部,它相当复杂,我想测试单个组件(例如输入解析器或验证器),但我不想公开这些组件。最终用户不需要知道这些。我知道这里的标准方法是"只测试您的公共API",但是我更喜欢测试代码的单一责任单元。这让我可以在不公开的情况下做到这一点。
测试私有方法的一种方法是通过反射。这也适用于Nunit和Xunit:
1 2 3 4
| MyObject objUnderTest = new MyObject ();
MethodInfo methodInfo = typeof(MyObject ).GetMethod("SomePrivateMethod", BindingFlags .NonPublic | BindingFlags .Instance);
object[] parameters = {"parameters here"};
methodInfo .Invoke(objUnderTest, parameters ); |
- call methods静态和非静态?
- 反射依赖方法的缺点是,当您使用r重命名方法时,它们往往会中断。对于小项目来说,这可能不是一个大问题,但是对于庞大的代码库,让单元测试以这种方式中断,然后不得不到处快速地修复它们,这有点让人恼火。从这个意义上说,我的钱都归杰夫了。
- @xds的name of()太差,无法从类外部获取私有方法的名称。
可以围绕包含要测试的私有方法的类创建包装类。这个包装类包含一个名为call-myprivatefunction的公共方法,该方法反过来调用其基类的私有函数。
请注意,方法的访问级别将更改为[受保护]
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Order
{
public int Quantity { get; set; }
protected bool OrderIsBig ()
{
//This is the method we want to test. It needs to be protected in stead of private. Else... no cigar
return Quantity > 100;
}
}
//Use this wrapper class in your unit test.
public class FakeOrder : Order
{
public bool Call_OrderIsBig()
{
//This makes the actual call to the protected method"OrderIsBig"
return OrderIsBig();
}
} |
单元测试代码可能如下所示:
1 2 3 4
| FakeOrder order = new FakeOrder ();
order .Quantity = 200;
bool isBig = order .Call_OrderIsBig(); //Make a call to a public method of the FakeOrder class which in turn makes a call to the protected method. |
Ermh…这里出现了完全相同的问题:测试一个简单但关键的私有方法。看完这条线后,似乎像是"我想在这块简单的金属上钻这个简单的孔,我想确保质量符合规格",然后出现"好吧,这不容易。首先,没有合适的工具可以做到这一点,但是你可以在你的花园里建造一个重力波观测站。请阅读我在http://foobar.brigther-than-einstein.org/上的文章。首先,当然,你必须参加一些高级量子物理课程,然后你需要大量的超冷氮,当然,还有我在亚马逊上的书"…
换句话说…
不,第一件事。
每一种方法,可以是私有的、内部的、受保护的、公共的,都必须是可测试的。必须有一种方法来实现这样的测试,而不需要像这里所介绍的那样做。
为什么?正是因为到目前为止一些贡献者提到了体系结构。也许对软件原则的简单重申可以消除一些误解。
在这种情况下,通常的嫌疑犯是:OCP、SRP和KIS。
但是等一下。让一切公开化的想法更多的是政治上的和一种态度。但是。当涉及到代码时,即使在当时的开源社区中,这也不是教条。相反,"隐藏"一些东西是一个很好的实践,可以使熟悉某个API变得更容易。例如,你会隐藏你新推出的面向市场的数字温度计构建块的核心计算——不是为了向好奇的代码阅读器隐藏实际测量曲线背后的数学,而是为了防止你的代码变得依赖于某些突然重要的用户,他们无法抗拒使用你以前的私有、内部和保护。TED代码来实现自己的想法。
我在说什么?
private double TranslateMeasurementIntoLinear(double actualMeasurement);
它很容易宣布水瓶座的年龄或什么是现在被称为,但如果我的一块传感器从1.0到2.0,执行翻译…可能会从一个简单的线性方程,很容易理解,对每个人都"可用",变为一个相当复杂的计算,使用分析或任何东西,所以我会破坏其他人的代码。为什么?因为他们不理解软件编码的原则,甚至连kis都不懂。
为了使这个童话故事简短化:我们需要一种简单的方法来测试私有方法——不用麻烦。
第一:大家新年快乐!
第二:排练你的建筑师课程。
第三,"公共"修饰语是宗教,不是解决问题的方法。
tl;dr:将私有方法提取到另一个类中,在该类上进行测试;了解更多有关srp原则(单一责任原则)的信息。
似乎您需要将private方法提取到另一个类;在这个类中应该是public。您应该测试另一个类的public方法,而不是在private方法上进行测试。
我们有以下场景:
1 2 3
| Class A
+ outputFile: Stream
- _someLogic(arg1, arg2) |
我们需要测试_someLogic的逻辑;但是看起来Class A的作用比它需要的要大(违反srp原则);只是重构成两个类。
1 2 3 4 5
| Class A1
+ A1(logicHandler: A2) # take A2 for handle logic
+ outputFile: Stream
Class A2
+ someLogic(arg1, arg2) |
这样,可以在a2上测试someLogic;在a1中,只需创建一些假的a2,然后注入到构造函数,以测试a2是否被调用到名为someLogic的函数。
在VS2005/2008中,您可以使用私有访问器来测试私有成员,但这种方法在较新版本的VS中消失了。
- 从2008年到2010年初,这是一个很好的答案。现在请参考privateobject和reflection选项(请参阅上面的几个答案)。VS2010有访问器错误,MS在VS2012中不赞成使用它。除非您被迫留在VS2010或更老的版本中(>18岁的构建工具),否则请避免使用专用访问器来节省时间。:-)