我是单元测试的新手,我想知道是否应该开始使用更多的 internal
访问修饰符。我知道如果我们使用 internal
并设置程序集变量 InternalsVisibleTo
,我们可以测试我们不想从测试项目中公开的函数。这让我认为我应该始终使用 internal
,因为至少每个项目(应该?)都有自己的测试项目。你们能告诉我为什么我不应该这样做吗?什么时候应该使用 private
?
System.Diagnostics.Debug.Assert()
来避免对内部方法进行单元测试的需要。
内部类需要测试,并且有一个程序集属性:
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("MyTests")]
将此添加到项目信息文件中,例如 Properties\AssemblyInfo.cs
,用于测试中的项目。在这种情况下,“MyTests”是测试项目。
如果您想测试私有方法,请查看 Microsoft.VisualStudio.TestTools.UnitTesting
命名空间中的 PrivateObject
和 PrivateType
。它们围绕必要的反射代码提供易于使用的包装器。
对于 VS2017 和 2019,您可以通过下载 MSTest.TestFramework nuget 找到这些
除了 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
AssemblyInfo
文件要干净得多。
我正在使用 .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>
您也可以使用私有方法,也可以使用反射调用私有方法。如果您使用的是 Visual Studio Team Suite,它具有一些不错的功能,可以生成代理来为您调用私有方法。这是一篇代码项目文章,演示了如何自己完成对私有和受保护方法的单元测试:
http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx
至于您应该使用哪个访问修饰符,我的一般经验法则是从私有开始并根据需要升级。这样,您将尽可能少地公开真正需要的类的内部细节,并有助于隐藏实现细节,因为它们应该是。
默认情况下继续使用私人。如果一个成员不应该暴露在那个类型之外,那么它不应该暴露在那个类型之外,即使在同一个项目中也是如此。这使事情变得更安全和更整洁 - 当您使用对象时,您可以更清楚地使用哪些方法。
话虽如此,我认为有时出于测试目的将自然私有方法设为内部是合理的。我更喜欢使用反射,因为它对重构不友好。
要考虑的一件事可能是“ForTest”后缀:
internal void DoThisForTest(string name)
{
DoThis(name);
}
private void DoThis(string name)
{
// Real implementation
}
然后,当您在同一个项目中使用该类时,很明显(现在和将来)您不应该真正使用这种方法 - 它只是用于测试目的。这有点老套,不是我自己做的,但至少值得考虑。
ForTest
方法,但我总是觉得它非常难看(添加在生产业务逻辑方面没有实际价值的代码)。通常我发现我不得不使用这种方法,因为设计有点不幸(即必须在测试之间重置单例实例)
ForTest
调用方法的任何代码显然是错误的,而如果您只是将方法设为内部,它看起来可以使用。
对于 .NET 核心,您可以将属性添加到命名空间作为 [程序集:InternalsVisibleTo("MyUnitTestsAssemblyName")]。例如类似的东西
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Applications.ExampleApp.Tests")]
namespace Applications.ExampleApp
internal sealed class ASampleClass : IDisposable
{
private const string ApiVersionPath = @"api/v1/";
......
......
......
}
}
在 .NET Core 2.2 中,将此行添加到 Program.cs:
using ...
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MyAssembly.Unit.Tests")]
namespace
{
...
将 InternalsVisibleTo.cs
文件添加到存在 .csproj
文件的项目的根文件夹中。
InternalsVisibleTo.cs
的内容应如下
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("AssemblyName.WhichNeedAccess.Example.UnitTests")]
不定期副业成功案例分享
private
的东西,但是太多的private
东西很可能指向一个难以提取的internal
类。 TDD 或没有 TDD,我更喜欢有更多的测试来测试大量的代码,而不是有很少的测试来执行相同数量的代码。避免测试internal
的东西并不能完全帮助实现良好的比率。#if DEBUG
、#endif
块将仅在调试版本中启用此选项。