C# “internal” access modifier when doing unit testing
我是一个新的单元测试人员,我想知道是否应该开始使用更多的"内部"访问修饰符。我知道,如果我们使用"internal"并设置程序集变量"internalsVisibleTo",我们可以测试不希望从测试项目中声明为public的函数。这让我觉得我应该总是使用"内部"的,因为至少每个项目(应该?)拥有自己的测试项目。你们能告诉我为什么我不能这样做吗?我什么时候应该使用"private"?
- 值得一提的是,通过在方法本身中使用System.Diagnostics.Debug.Assert(),您通常可以避免单元测试内部方法的需要。
需要测试内部类,并且有assemby属性:
1 2 3
| using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("MyTests")] |
将其添加到项目信息文件中,例如Properties\AssemblyInfo.cs。
- 我是将此添加到测试项目还是添加到正在测试的项目中!?!?
- 将其添加到正在测试的项目中(例如,在propertiesassemblyinfo.cs中)。"mytests"将是测试组件。
- 这应该是公认的答案。我不知道你们的情况,但当测试离代码"太远"时,他们正在测试,我往往会紧张。我完全赞成避免测试任何标记为private的东西,但是太多private的东西可能很好地指向一个internal类,这个类正在努力被提取。不管是TDD还是非TDD,我更喜欢有更多的测试来测试大量的代码,而不是只有很少的测试可以执行相同数量的代码。并且避免测试internal的东西并不能帮助实现一个好的比率。
- 谢谢埃里克。整天都在寻找这个选择。
- 我用和if release指令包装了这个属性,这样在发布构建中内部就不会对任何东西可见。只是一个想法。
- @S.M.完全同意……另外,测试UI代码几乎是不可能的,所以我通常将很多"自定义"的UI布局逻辑推迟到我可以测试的方法中,而不必运行WPF或WinForms应用程序并查看结果(即元素的排序、检查位置信息等)。这个逻辑是明确标记的。由于内部和绝对不能通过"公开"来测试,这意味着……当人们把"公开"和"可测试"联系起来时,我无法忍受,他们不是一个整体,每种情况都是一样的……我是TDD的倡导者……
- @derickbailey和dan tao就内部和私有的语义差异以及测试内部组件的需求进行了大量讨论。值得一读。
- 封装和#if DEBUG,#endif块将仅在调试版本中启用此选项。
- 我必须在哪里添加内部可见?主项目或测试项目assemblyinfo类。
- 如第二条注释中所述:在您想要测试的项目中。
- @cadbloke这是否意味着您不针对发布版本运行单元测试?我们为调试和发布版本运行单元测试。devs在本地运行调试生成的单元测试,build server为发布版本运行它们。确保我们捕获调试和发布代码之间的差异,这是由于两者的编译设置不同(例如优化)。
- @Marjan这是一个很好的观点,是的,我改变了我这样做的方式,我测试了发布版本,然后评论了部署版本的InternalsVisibleTo代码。如果您提交代码并在此时标记它,那么您将保留该部署的里程碑,然后恢复注释的代码并继续执行。理论上,这可能会有副作用,但总比不测试发布代码和带有潜在安全漏洞的运输代码要好。
- @球童酷。InternalsVisibleTo是否是一个安全孔,因为具有指定名称的任何程序集都可以访问内部?我认为.NET本质上是如此不安全-因为反射和所有的(IL)反汇编程序-我不会花费精力删除它进行发布构建…我是不是错过了什么?
- @Marjan是的,除非您对程序集进行强名称签名并在InternalsVisibleTo属性中包含键。请参阅adelkhalil.bloggingabout.net/2007/10/12/…我想这取决于你的偏执程度。
- @ CADbloke:啊。好啊。谢谢你的链接。阅读…
- 这是正确的答案。任何说只有公共方法才应该进行单元测试的回答都是缺少单元测试的要点和借口。功能测试是面向黑盒的。单元测试是面向白盒的。他们应该测试功能的"单元",而不仅仅是公共API。
- 对于.NET核心-将其添加到应用程序中的任何.cs文件中。请参阅此处的详细信息-stackoverflow.com/a/42235577/968003
- 亚历克斯克劳斯补充说,作为一个答案。我只是花了一段时间来挖掘。.NET核心足够新,问题需要新的答案。
- 很好的内部课程解决方案!但在VS2017中,我仍然收到错误消息,即VS无法为此类类创建单元测试。我必须手动创建一个。
- 从我的观点来看,这是一个错误的答案。内部应该保持这样,没有一个标志允许它们是内部的,这取决于调用方。
- 试图找出在InternalsVisibleTo参数中替换"MyTests"参数的方法。我的测试项目叫做api.IntegrationTests。会是[assembly: InternalsVisibleTo("api.IntegrationTests")]吗??
- @Kylevassella包含测试的程序集的名称。
如果要测试私有方法,请查看PrivateObject和PrivateType命名空间中的Microsoft.VisualStudio.TestTools.UnitTesting。它们在必要的反射代码周围提供易于使用的包装器。
Docs:私有类型,私有对象
- 投票失败时,请发表评论。谢谢。
- 投票否决这个答案是愚蠢的。它指出了一个新的解决方案和一个以前没有提到的非常好的解决方案。
默认情况下保持使用私密。如果一个成员不应该暴露在该类型之外,那么它不应该暴露在该类型之外,甚至是在同一个项目中。这样可以使事情更安全、更整洁——当你使用这个对象时,你应该能够使用的方法就更清楚了。
尽管如此,我认为有时出于测试目的,将自然私有的方法作为内部方法是合理的。我更喜欢使用反射,这是重构不友好。
要考虑的一件事可能是"fortest"后缀:
1 2 3 4 5 6 7 8 9
| internal void DoThisForTest(string name)
{
DoThis(name);
}
private void DoThis(string name)
{
// Real implementation
} |
然后,当您在同一个项目中使用这个类时,很明显(现在和将来)您不应该真正使用这个方法——它只是用于测试目的。这有点老土,不是我自己做的,但至少值得考虑。
- 如果该方法是内部的,是否不排除其在测试组件中的使用?
- @拉尔夫:不使用InternalsVisibleTo。
- 我偶尔会使用ForTest方法,但我总是发现它非常难看(添加的代码在生产业务逻辑方面没有实际价值)。通常我发现我不得不使用这种方法,因为设计非常不幸(即必须在测试之间重置单例实例)。
- 试图推翻这一点-这一黑客和简单地将类设置为内部而不是私有有什么区别?好吧,至少在编译条件下。然后就变得很乱了。
- @cadbloke:你的意思是让方法内部化而不是私有化?区别在于,很明显你真的希望它是私人的。生产代码库中调用带有ForTest的方法的任何代码都明显是错误的,但是如果您只是将该方法设置为内部方法,则使用起来似乎很好。
- 谢谢,我明白了,但我担心方法本身现在可以从更广泛的范围内访问。假设范围是"内部的",那么它仍然是安全的,不会受到毫无戒心的公众的窥探,所以没有投反对票(我的假设有点偏离了目标)。我在其他地方看到了一个建议使用分部类的答案,如果有人习惯使用这种方法的话。再次感谢您抽出时间回答。:)
- @凯德布洛克:局部类在这里也不会有什么不同。最好的是一个属性,静态分析工具可以使用该属性检查它是否不是从非测试程序集使用的。
- 在这种情况下使用分部类的唯一原因是隐藏混乱并将其从发布构建中排除。
- @cadbloke:在同一个文件中,你可以像使用分部类(imo)一样轻松地排除发布版本中的单个方法。如果你这样做,这就意味着你没有对发布版本运行测试,这对我来说是个坏主意。
您也可以使用private,并且可以使用反射调用private方法。如果您使用的是Visual Studio团队套件,它有一些很好的功能,可以生成一个代理来调用您的私有方法。下面是一篇代码项目文章,演示如何自己完成单元测试私有和受保护方法的工作:
http://www.codeproject.com/kb/cs/testnopublicmembers.aspx
关于应该使用哪个访问修饰符,我的一般经验法则是从private开始,并根据需要升级。通过这种方式,您将只公开真正需要的类的内部细节,这有助于保持实现细节的隐藏,因为它们应该是隐藏的。
添加到Eric的答案中,您还可以在csproj或Directory.Build.props文件中通过添加:
1 2 3 4 5
| <ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
</AssemblyAttribute>
</ItemGroup> |
请参阅:https://stackoverflow.com/a/49978185/1678053