进行单元测试时的C#"内部"访问修饰符

Her*_*Lie 419 .net c# tdd unit-testing

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

Eri*_*fer 1121

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

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]
Run Code Online (Sandbox Code Playgroud)

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

  • 这应该是公认的答案.我不了解你们,但是当他们测试的代码"太远"时,我会感到紧张.我完全是为了避免测试标记为"私有"的东西,但是太多的"私有"东西可能很好地指向一个正在努力被提取的"内部"类.TDD或没有TDD,我更喜欢测试大量代码的测试,而不是少量测试可以运行相同数量的代码.并且避免测试"内部"东西并不能完全帮助实现良好的比例. (84认同)
  • 将其添加到测试项目中(例如,在Properties\AssemblyInfo.cs中)."MyTests"将成为测试组件. (66认同)
  • 包装和`#if DEBUG`,`#endif`块只在调试版本中启用此选项. (26认同)
  • 这是正确的答案.任何答案都说只有公共方法应该进行单元测试才会错过单元测试和借口.功能测试是面向黑盒的.单元测试是面向白盒的.他们应该测试功能的"单位",而不仅仅是公共API. (12认同)
  • 有一个很棒的[讨论](http://lostechies.com/derickbailey/2014/01/03/semantics-modules-and-testing-why-and-how-i-test-internal-components-not-private-methods /)在@DerickBailey和Dan Tao之间继续讨论**内部**和**私有**之间的语义差异以及测试_internal_组件的必要性.非常值得一读. (5认同)
  • 对于.NET Core - 将其添加到应用程序中的任何.cs文件.详情请见/sf/answers/2956490421/ (5认同)
  • 我是将它添加到测试项目还是测试项目中??!? (4认同)
  • @Marjan是的,除非您对强化名称签署程序集并将密钥包含在InternalsVisibleTo属性中.请参阅http://adelkhalil.bloggingabout.net/2007/10/12/tiny-tip-improve-security-with-internalsvisibleto-attribute/我想这取决于你的偏执程度. (3认同)
  • 谢谢Eric.一整天都在寻找那个选项. (2认同)
  • 我使用#if RELEASE指令包装了这个属性,因此内部版本对于发布版本中的任何内容都不可见.只是一个想法. (2认同)
  • @sm 完全同意...另外,测试 UI 代码几乎是不可能的,所以我通常将许多“自定义”UI 布局逻辑推迟到我可以测试的方法中,而不必运行 WPF 或 WinForms 应用程序并查看结果(即元素的排序,检查位置信息等...等等)这个逻辑肯定被标记为内部的,并且绝对*不能*通过“公共”方式进行测试...我无法忍受人们将“公共”与“可测试”联系起来,它们*不是*对于每一个案例都是一样的......而我是 TDD 的倡导者...... (2认同)
  • 亚历克斯·克劳斯(Alex Klaus)补充说。我只是花了一些时间来寻找。.NET Core足够新,问题需要一个新的答案。 (2认同)

Bri*_*sen 118

如果你想测试私有方法,看一看PrivateObject,并PrivateTypeMicrosoft.VisualStudio.TestTools.UnitTesting命名空间.它们围绕必要的反射代码提供易于使用的包装器.

文档: PrivateType,PrivateObject

  • 这个答案是愚蠢的投票.它指出了一种新的解决方案和一种前所未有的非常好的解决方案. (32认同)
  • 投票时请发表评论.谢谢. (12认同)

ihe*_*heb 82

从 .NET 5 开始,您还可以在csproj正在测试的项目的文件中使用此语法:

  <ItemGroup>
    <InternalsVisibleTo Include="MyProject.Tests" />
  </ItemGroup>
Run Code Online (Sandbox Code Playgroud)

  • 该解决方案效果很好,并且是代码较少的解决方案 ++1 (2认同)

gld*_*ael 20

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

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)

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

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)

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

  • 不错,但已经过时了。请改用 `&lt;InternalsVisibleTo&gt;`,如 [ihebiheb 的答案](/sf/answers/5128271711/) 中所述。 (6认同)
  • 在我看来,这应该是最佳答案。所有其他答案都非常过时,因为 .net 正在远离程序集信息并将功能移至 csproj 定义中。 (4认同)
  • 非常好的答案!这比必须添加此“AssemblyInfo”文件要干净得多。 (2认同)

Flo*_*ish 12

我正在使用.NET Core 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>
Run Code Online (Sandbox Code Playgroud)

  • 添加显式生成程序集信息最终也使其对我有用。感谢您发布此内容! (2认同)

Ste*_*nke 11

您也可以使用私有,您可以使用反射调用私有方法.如果您使用的是Visual Studio Team Suite,它有一些很好的功能,可以生成一个代理来为您调用私有方法.这是一个代码项目文章,演示了如何自己完成工作以对私有和受保护方法进行单元测试:

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

就您应该使用的访问修饰符而言,我的一般经验法则是私有,并根据需要升级.这样,您将尽可能少地公开您的类的内部细节,它有助于保持实现细节隐藏,就像它们应该的那样.


Jon*_*eet 10

默认情况下继续使用私有.如果成员不应暴露在该类型之外,则不应暴露在该类型之外,即使是在同一项目中也是如此.这样可以使事情变得更加安全和整洁 - 当您使用该对象时,您可以使用哪种方法更清晰.

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

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

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

private void DoThis(string name)
{
    // Real implementation
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我偶尔会使用`ForTest`方法但我总觉得它很难看(添加代码在生产业务逻辑方面没有提供任何实际价值).通常我发现我必须使用这种方法,因为设计很不幸(即必须在测试之间重置单例实例) (5认同)
  • @CADbloke:你的意思是制作*方法*内部而不是私有?不同之处在于,你真的希望*它是私有的.你的生产代码库中用`ForTest`调用方法的任何代码*显然是错误的,而如果你只是将方法设为内部,那么它看起来好像可以使用. (5认同)
  • 如果该方法是内部方法,这是否不排除将其用于测试程序集? (2认同)
  • 很想对此投反对票 - 这种黑客行为与简单地将类设为内部类而不是私有类有什么区别?好吧,至少有编译条件。然后事情就会变得非常混乱。 (2认同)
  • @CADbloke:您可以在发布版本中排除单个方法,就像使用部分类IMO一样容易.如果你*做*那么,它表明你没有针对你的发布版本运行测试,这对我来说听起来不错. (2认同)