进行单元测试时使用C#“内部”访问修饰符


469

我是单元测试的新手,我想弄清楚是否应该开始使用更多的“内部”访问修饰符。我知道,如果我们使用“内部”并设置汇编变量“ InternalsVisibleTo”,那么我们可以测试我们不想从测试项目中声明为公共的函数。这使我认为我应该始终使用“内部”,因为至少每个项目(应该?)都有它自己的测试项目。你们能告诉我为什么我不这样做的原因吗?我什么时候应该使用“私人”?


1
值得一提的是,您通常可以通过System.Diagnostics.Debug.Assert()在方法内部使用来避免对内部方法进行单元测试的需要。
Mike Marynowski

Answers:


1195

内部类需要测试,并且有一个assemby属性:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

将此添加到项目信息文件,例如Properties\AssemblyInfo.cs


70
将其添加到测试中的项目(例如,在Properties \ AssemblyInfo.cs中)。“ MyTests”将是测试程序集。
EricSchaefer 2010年

86
这确实应该是公认的答案。我不了解你们,但是当测试与他们正在测试的代码“相距太远”时,我会感到紧张。我全力避免测试标记为的任何东西private,但是太多的private事情很可能指向一个internal难以提取的类。TDD还是没有TDD,我宁愿拥有更多测试大量代码的测试,而不是拥有很少数量执行相同数量代码的测试。避免测试internal东西并不能完全帮助获得良好的比率。
2012年

7
@DerickBailey和Dan Tao之间就内部私有之间的语义差异以及测试内部组件的需求进行了广泛的讨论。非常值得一读。
克里斯·麦金尼斯

31
在包装和#if DEBUG#endif块将启用该选项仅在调试版本。
真正的爱德华·卡伦2014年

17
这是正确的答案。任何说只有公共方法应该进行单元测试的答案都缺少单元测试的意义,并以此为借口。功能测试是面向黑匣子的。单元测试是面向白盒的。他们应该测试功能的“单元”,而不仅仅是公共API。
Gunnar

127

如果要测试私有方法,请查看PrivateObjectPrivateTypeMicrosoft.VisualStudio.TestTools.UnitTesting命名空间中。它们为必要的反射代码提供了易于使用的包装器。

文件: PrivateTypePrivateObject

对于VS2017和2019,您可以通过下载MSTest.TestFramework nuget找到这些


15
当投反对票时,请发表评论。谢谢。
Brian Rasmussen

35
拒绝这个答案真是愚蠢。它指出了一种新的解决方案,并且是之前未提及的非常好的解决方案。
Ignacio Soler Garcia'9

显然,使用TestFramework来定位.net2.0或更高版本的应用存在一些问题:github.com/Microsoft/testfx/issues/366
Johnny Wu

41

除Eric的答案外,您还可以在csproj文件中进行配置:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

或者,如果每个要测试的项目都有一个测试项目,则可以在Directory.Build.props文件中执行以下操作:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

参见:https : //stackoverflow.com/a/49978185/1678053
示例:https : //github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12


2
这应该是imo的最佳答案。当.net远离程序集信息并将功能转移到csproj定义中时,所有其他答案都已过时。
mBrice1024

12

默认情况下继续使用私有。如果成员不应该暴露于该类型之外,就不应暴露于该类型之外,甚至不暴露于同一项目内。这样可以使事情更安全,更整洁-使用对象时,您可以使用的方法更加清楚。

话虽如此,我认为有时出于测试目的将内部私有方法设为内部是合理的。我更喜欢使用反射,它是重构不友好的。

要考虑的一件事可能是“ ForTest”后缀:

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

然后,当您在同一个项目中使用该类时,很明显(现在和将来)您实际上不应该使用此方法-它仅用于测试目的。这有点棘手,不是我自己做的事,但这至少值得考虑。


2
如果该方法是内部方法,这是否不排除将其用于测试程序集?
拉尔夫·希灵顿

7
我偶尔使用这种ForTest方法,但是我总是觉得它很丑陋(添加的代码在生产业务逻辑方面没有任何实际价值)。通常,我发现我不得不使用该方法,因为该设计有点不幸(即必须在测试之间重置单例实例)
ChrisWue 2012年

1
试图拒绝投票-这种黑客与简单地将类设为内部而非私有之间有什么区别?好吧,至少在编译条件下。然后,它变得非常混乱。
CAD家伙

6
@CADbloke:您的意思是将方法内部化而不是私有化吗?所不同的是,很明显您确实希望它是私有的。您的生产代码库中的任何代码调用方法与ForTest明显错误的,而如果你只是使该方法内部它看起来像它的优良使用。
乔恩·斯基特

2
@CADbloke:您可以在发布文件中排除单个方法,就像在使用IMO的部分类中一样容易。而且,如果这样做,则表明您未针对发行版运行测试,这对我来说是个坏主意。
乔恩·斯基特

11

您也可以使用私有方法,并且可以通过反射调用私有方法。如果您使用的是Visual Studio Team Suite,它将具有一些不错的功能,它将生成一个代理来为您调用私有方法。这是一个代码项目文章,该文章演示了如何自己完成单元测试私有和受保护方法的工作:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

关于应该使用哪种访问修饰符,我的一般经验法则是从private开始,然后根据需要进行升级。这样,您将仅暴露真正需要的类的内部细节,并且有助于隐藏实现细节(应将其隐藏)。


3

我正在使用Dotnet 3.1.101,对我有用的.csproj其他功能是:

<PropertyGroup>
  <!-- Explicitly generate Assembly Info -->
  <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
  <_Parameter1>MyProject.Tests</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

希望这可以帮助到那里的人!


1
显式生成装配体信息的添加最终使它也对我有用。感谢您发布!
thevioletsaber
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.