C#中的单元测试私有方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9122708/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unit testing private methods in C#
提问by junichiro
Visual Studio allows unit testing of private methods via an automatically generated accessor class. I have written a test of a private method that compiles successfully, but it fails at runtime. A fairly minimal version of the code and the test is:
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
}
The runtime error is:
运行时错误是:
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]'.
According to intellisense - and hence I guess the compiler - target is of type TypeA_Accessor. But at runtime it is of type TypeA, and hence the list add fails.
根据智能感知 - 因此我猜编译器 - 目标是 TypeA_Accessor 类型。但是在运行时它是 TypeA 类型,因此列表添加失败。
Is there any way I can stop this error? Or, perhaps more likely, what other advice do other people have (I predict maybe "don't test private methods" and "don't have unit tests manipulate the state of objects").
有什么办法可以阻止这个错误吗?或者,也许更有可能的是,其他人有什么其他建议(我预测可能“不要测试私有方法”和“不要让单元测试操纵对象的状态”)。
采纳答案by Keith Nicholas
Yes, don't Test private methods.... The idea of a unit test is to test the unit by its public 'API'.
是的,不要测试私有方法......单元测试的想法是通过其公共“API”来测试单元。
If you are finding you need to test a lot of private behavior, most likely you have a new 'class' hiding within the class you are trying to test, extract it and test it by its public interface.
如果你发现你需要测试很多私有行为,很可能你有一个新的“类”隐藏在你试图测试的类中,提取它并通过它的公共接口测试它。
One piece of advice / Thinking tool..... There is an idea that no method should ever be private. Meaning all methods should live on a public interface of an object.... if you feel you need to make it private, it most likely lives on another object.
一条建议/思考工具..... 有一种想法是任何方法都不应该是私有的。这意味着所有方法都应该存在于对象的公共接口上……如果您觉得需要将其设为私有,它很可能存在于另一个对象上。
This piece of advice doesn't quite work out in practice, but its mostly good advice, and often it will push people to decompose their objects into smaller objects.
这条建议在实践中并不完全有效,但它主要是好的建议,并且通常会促使人们将他们的对象分解为更小的对象。
回答by Jeff
Another thought here is to extend testing to "internal" classes/methods, giving more of a white-box sense of this testing. You can use InternalsVisibleToAttribute on the assembly to expose these to separate unit testing modules.
这里的另一个想法是将测试扩展到“内部”类/方法,为这种测试提供更多的白盒意义。您可以在程序集上使用 InternalsVisibleToAttribute 将这些公开给单独的单元测试模块。
In combination with sealed class you can approach such encapsulation that test method are visible only from unittest assembly your methods. Consider that protected method in sealed class is de facto private.
结合密封类,您可以进行这样的封装,即测试方法仅从您的方法的单元测试组件中可见。考虑到密封类中的受保护方法实际上是私有的。
[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()
{
}
}
}
And unit test:
和单元测试:
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
}
}
回答by Scuttle
You can use PrivateObject Class
您可以使用PrivateObject 类
Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(expectedVal, retVal);
回答by Shivprasad Ktheitroadala
“There is nothing called as standard or best practice, probably they are just popular opinions”.
“没有什么叫做标准或最佳实践,可能它们只是流行的意见”。
Same holds true for this discussion as well.
本次讨论也是如此。


It all depends on what you think is a unit , if you think UNIT is a class then you will only hit the public method. If you think UNIT is lines of code hitting private methods will not make you feel guilty.
这一切都取决于你认为什么是单元,如果你认为 UNIT 是一个类,那么你只会命中公共方法。如果您认为 UNIT 是命中私有方法的代码行,您不会感到内疚。
If you want to invoke private methods you can use "PrivateObject" class and call the invoke method. You can watch this indepth youtube video ( http://www.youtube.com/watch?v=Vq6Gcs9LrPQ) which shows how to use "PrivateObject" and also discusses if testing of private methods are logical or not.
如果要调用私有方法,可以使用“PrivateObject”类并调用 invoke 方法。您可以观看这个深入的 youtube 视频 ( http://www.youtube.com/watch?v=Vq6Gcs9LrPQ),该视频展示了如何使用“PrivateObject”,还讨论了私有方法的测试是否合乎逻辑。
回答by Allen
In VS 2005/2008 you can use private accessorto test private member,but this way was disappearin later version of VS
在 VS 2005/2008 中你可以使用私有访问器来测试私有成员,但这种方式在 VS 的更高版本中消失了
回答by Hyman Davidson
One way to test private methods is through reflection. This applies to NUnit and XUnit, too:
测试私有方法的一种方法是通过反射。这也适用于 NUnit 和 XUnit:
MyObject objUnderTest = new MyObject();
MethodInfo methodInfo = typeof(MyObject).GetMethod("SomePrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
object[] parameters = {"parameters here"};
methodInfo.Invoke(objUnderTest, parameters);
回答by o0omycomputero0o
TL;DR:Extract private method to another class, test on that class; read more about SRP principle (Single Responsibility Principle)
TL;DR:将私有方法提取到另一个类,在该类上进行测试;阅读更多关于 SRP 原则(单一职责原则)
It seem that you need extract to the privatemethod to another class; in this should be public. Instead of trying to test on the privatemethod, you should test publicmethod of this another class.
看来您需要将private方法提取到另一个类中;在这应该是public。private您应该测试public另一个类的方法,而不是尝试测试该方法。
We has the following scenario:
我们有以下场景:
Class A
+ outputFile: Stream
- _someLogic(arg1, arg2)
We need to test the logic of _someLogic; but it seem that Class Atake more role than it need(violate the SRP principle); just refactor into two classes
我们需要测试的逻辑_someLogic;但似乎Class A扮演了比需要更多的角色(违反了 SRP 原则);只需重构为两个类
Class A1
+ A1(logicHandler: A2) # take A2 for handle logic
+ outputFile: Stream
Class A2
+ someLogic(arg1, arg2)
In this way someLogiccould be test on A2; in A1 just create some fake A2 then inject to constructor to test that A2 is called to the function named someLogic.
这样someLogic就可以在A2上进行测试;在 A1 中只需创建一些假 A2 然后注入构造函数以测试 A2 被调用到名为someLogic.
回答by CP70
Ermh... Came along here with exactly the same problem: Test a simple, but pivotal privatemethod. After reading this thread, it appears to be like "I want to drill this simple hole in this simple piece of metal, and I want to make sure the quality meets the specs", and then comes "Okay, this is not to easy. First of all, there is no proper tool to do so, but you could build a gravitational-wave observatory in your garden. Read my article at http://foobar.brigther-than-einstein.org/First, of course, you have to attend some advanced quantum physics courses, then you need tons of ultra-cool nitrogenium, and then, of course, my book available at Amazon"...
呃...来到这里遇到完全相同的问题:测试一个简单但关键的私有方法。读完这个帖子后,它似乎是“我想在这块简单的金属上钻这个简单的孔,我想确保质量符合规格”,然后是“好吧,这并不容易。首先,没有合适的工具可以这样做,但是你可以在你的花园里建造一个引力波天文台。阅读我的文章http://foobar.brigther-than-einstein.org/首先,当然,你必须参加一些高级量子物理课程,然后你需要大量的超冷氮,然后,当然,我的书可以在亚马逊上买到”...
In other words...
换句话说...
No, first things first.
不,第一件事。
Each and every method, may it private, internal, protected, public hasto be testable. There has to be a way to implement such tests without such ado as was presented here.
每一种方法,无论是私有的、内部的、受保护的、公共的,都必须是可测试的。必须有一种方法来实现这样的测试,而不像这里介绍的那样麻烦。
Why? Exactly becauseof the architectural mentions done so far by some contributors. Perhaps a simple reiteration of software principles may clear up some missunderstandings.
为什么?正是因为一些贡献者迄今为止所做的架构提及。也许简单地重申软件原则可能会消除一些误解。
In this case, the usual suspects are: OCP, SRP, and, as always, KIS.
在这种情况下,通常的嫌疑人是:OCP、SRP 和一如既往的 KIS。
But wait a minute. The idea of making everything publicly available is more of less political and a kind of an attitude. But. When it comes to code, even in then Open Source Community, this is no dogma. Instead, "hiding" something is good practice to make it easier to come familiar with a certain API. You would hide, for example, the very core calculations of your new-to-market digital thermometer building block--not to hide the maths behind the real measured curve to curious code readers, but to prevent your code from becoming dependent on some, perhaps suddenly important users who could not resist using your formerly private, internal, protected code to implement their own ideas.
但是等一下。将所有内容都公开的想法更多的是不那么化,是一种态度。但。说到代码,即使在当时的开源社区中,这也不是教条。相反,“隐藏”某些东西是一种很好的做法,可以让您更容易熟悉某个 API。例如,您会隐藏新上市的数字温度计构建块的核心计算——不是向好奇的代码阅读器隐藏真实测量曲线背后的数学,而是为了防止您的代码变得依赖于某些,也许突然之间重要的用户无法抗拒使用您以前私有的、内部的、受保护的代码来实现他们自己的想法。
What am I talking about?
我在说什么?
private double TranslateMeasurementIntoLinear(double actualMeasurement);
私人双 TranslateMeasurementIntoLinear(双实际测量);
It's easy to proclaim the Age of Aquarius or what is is been called nowadays, but if my piece of sensor gets from 1.0 to 2.0, the implementation of Translate... might change from a simple linear equation that is easily understandable and "re-usable" for everybody, to a pretty sophisticated calculation that uses analysis or whatever, and so I would break other's code. Why? Because they didn't understand the very priciples of software coding, not even KIS.
宣布水瓶座时代或现在所谓的时代很容易,但如果我的传感器从 1.0 升级到 2.0,Translate...对每个人都可用”,到使用分析或其他任何东西的非常复杂的计算,所以我会破坏其他人的代码。为什么?因为他们不了解软件编码的基本原理,甚至不了解 KIS。
To make this fairy tale short: We need a simpleway to test private methods--without ado.
为了让这个童话故事简短:我们需要一种简单的方法来测试私有方法——事不宜迟。
First: Happy new year everyone!
第一:祝大家新年快乐!
Second: Rehearse your architect lessons.
第二:排练你的建筑师课程。
Third: The "public" modifier is religion, not a solution.
第三:“公共”修饰符是宗教,而不是解决方案。
回答by Roger Hill
Another option that has not been mentioned is just creating the unit test class as a child of the object that you are testing. NUnit Example:
另一个未提及的选项是将单元测试类创建为您正在测试的对象的子类。NUnit 示例:
[TestFixture]
public class UnitTests : ObjectWithPrivateMethods
{
[Test]
public void TestSomeProtectedMethod()
{
Assert.IsTrue(this.SomeProtectedMethod() == true, "Failed test, result false");
}
}
This would allow easy testing of private and protected (but not inherited private) methods, and it would allow you to keep all your tests separate from the real code so you aren't deploying test assemblies to production. Switching your private methods to protected methods would be acceptable in a lot of inherited objects, and it is a pretty simple change to make.
这将允许轻松测试私有和受保护(但不是继承的私有)方法,并且它允许您将所有测试与实际代码分开,这样您就不会将测试程序集部署到生产中。在许多继承的对象中,将私有方法切换为受保护的方法是可以接受的,这是一个非常简单的更改。
HOWEVER...
然而...
While this is an interesting approach to solving the problem of how to test hidden methods, I am unsure that I would advocate that this is the correct solution to the problem in all cases. It seems a little odd to be internally testing an object, and I suspect there might be some scenarios that this approach will blow up on you. (Immutable objects for example, might make some tests really hard).
虽然这是解决如何测试隐藏方法的问题的有趣方法,但我不确定我是否会主张这在所有情况下都是问题的正确解决方案。在内部测试一个对象似乎有点奇怪,我怀疑在某些情况下这种方法可能会让你失望。(例如,不可变对象可能会使某些测试变得非常困难)。
While I mention this approach, I would suggest that this is more of a brainstormed suggestion than a legitimate solution. Take it with a grain of salt.
虽然我提到了这种方法,但我认为这更像是一个集思广益的建议,而不是一个合法的解决方案。把它和一粒盐一起吃。
EDIT: I find it truly hilarious that people are voting this answer down, since I explicitly describe this as a bad idea. Does that mean that people are agreeing with me? I am so confused.....
编辑:我发现人们对这个答案投反对票真的很有趣,因为我明确地将其描述为一个坏主意。这是否意味着人们同意我的观点?我感到很困惑.....
回答by Kai Hartmann
From the book Working Effectively with Legacy Code:
来自《有效地使用遗留代码》一书:
"If we need to test a private method, we should make it public. If making it public bothers us, in most cases, it means that our class is doing too much and we ought to fix it."
“如果我们需要测试一个私有方法,我们应该将它公开。如果公开它让我们感到困扰,在大多数情况下,这意味着我们的类做得太多,我们应该修复它。”
The way to fix it, according to the author, is by creating a new class and adding the method as public.
根据作者的说法,修复它的方法是创建一个新类并将方法添加为public.
The author explains further:
作者进一步解释:
"Good design is testable, and design that isn't testable is bad."
“好的设计是可测试的,不可测试的设计是糟糕的。”
So, within these limits, your only real option is to make the method public, either in the current or a new class.
因此,在这些限制内,您唯一真正的选择是public在当前类或新类中创建方法。

