使代码内部化,但可用于其他项目的单元测试
Posted
技术标签:
【中文标题】使代码内部化,但可用于其他项目的单元测试【英文标题】:Making code internal but available for unit testing from other projects 【发布时间】:2010-09-11 12:53:54 【问题描述】:我们将所有单元测试都放在他们自己的项目中。我们发现我们必须将某些类公开而不是仅用于单元测试的内部类。无论如何要避免这样做。公开类而不是密封类对内存的影响是什么?
【问题讨论】:
C# "internal" access modifier when doing unit testing的可能重复 【参考方案1】:如果您使用的是 .NET,InternalsVisibleTo 程序集属性允许您创建“朋友”程序集。这些是特定的强命名程序集,允许访问其他程序集的内部类和成员。
注意,这应该谨慎使用,因为它会紧密耦合所涉及的程序集。 InternalsVisibleTo 的一个常见用途是用于单元测试项目。由于上述原因,在您的实际应用程序集中使用它可能不是一个好的选择。
示例:
[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
【讨论】:
我建议在属性周围放置一个#if DEBUG,然后在调试中进行单元测试。这样您就可以确定该属性未在发布代码上设置。 这只是一个想法,我不知道.... 怎么样:#if DEBUG public class IniReader #else internal class IniReader #endif 可能不推荐?为什么? 好吧,为什么要将测试限制在调试版本上? 另外,只是一个挑剔,没有必要对“朋友”程序集进行强烈命名(可能是一个好习惯 - 即使我的个人喜好另有说明,但不是强制性的)。 不同意。如果我正在使用非常薄的公共 API 构建复杂的组件,那么仅通过公共 API 进行测试是不切实际和不现实的。你最终会得到一个无法维护的泥球。相反,我会仔细定义内部单元并分别测试它们。正如 Jeremy D. Miller 经常说的:“先做小事,再做大事”。【参考方案2】:如果它是一个内部类,那么它一定不能被孤立地使用。因此,除了测试其他在内部使用该对象的类之外,您不应该真正对其进行测试。
正如您不应该测试类的私有成员一样,您也不应该测试 DLL 的内部类。这些类是某些可公开访问的类的实现细节,因此应该通过其他单元测试很好地进行练习。
这个想法是您只想测试一个类的行为,因为如果您测试内部实现细节,那么您的测试将会很脆弱。您应该能够在不破坏所有测试的情况下更改任何类的实现细节。
如果您发现您确实需要测试该类,那么您可能需要重新检查为什么该类首先是内部的。
【讨论】:
实施细节应该作为包含测试的一部分来执行。不要偷看私有变量...测试预期的行为。如果测试是正确的..所有内部管道和布线都应作为其中的一部分进行测试。投票赞成。 我不一定同意这一点,因为这些类对 DLL 中的其他类是“公共的”,并且应该独立测试该类的功能 我也不同意。单位就是单位,必须单独测试。 伟大的一般指导方针,但我们不要教条。测试有两个主要目的:1)回归——确保你没有破坏某些东西,2)提高开发速度。如果每次我想编写一行代码时都必须支持一项庞大的服务,我将阻碍开发。如果我有一个复杂的内部部件,我希望能够独立开发和测试。相反,我不希望所有东西都被隔离测试(从而降低回归测试值,或者重复测试代码),但是一些代码证明了隔离是合理的。也许关键是让它成为一个单独的程序集。 这里的操作是正确的。您正在将测试与内部实现耦合。因此,您的团队永远是修补测试和可怕的模拟代码的奴隶。仅测试公共 API,无论是打包的 lib API 还是网络公开的 API。所有代码都应该可以通过前门执行。测试实现是 TDD 在很大程度上死亡的原因,它只是一个巨大的 PITA 来测试整个应用程序的每个类,而不是专注于确保系统的行为。我想几乎每个人,包括“权威”书籍,都犯了这个错误或没有说清楚。【参考方案3】:用于文档目的
您也可以使用Type.GetType
方法实例化内部类
例子
//IServiceWrapper is public class which is
//the same assembly with the internal class
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
.CreateInstance(type, new object[1] /*constructor parameter*/ );
对于泛型类型,有如下不同的处理:
var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] typeof(T) );
return (IServiceWrapper<T>)Activator
.CreateInstance(genType, new object[1] /*constructor parameter*/);
【讨论】:
【参考方案4】:以下是在 .NET Core 应用程序中使用的方法。
-
添加AssemblyInfo.cs文件并添加
[assembly: InternalsVisibleTo("AssemblytoVisible")]
将此添加到 .csproj 文件中(包含内部类的项目)
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Test_Project_Name</_Parameter1> <!-- The name of the project that you want the Internal class to be visible To it -->
</AssemblyAttribute>
</ItemGroup>
更多信息请关注https://improveandrepeat.com/2019/12/how-to-test-your-internal-classes-in-c/
【讨论】:
【参考方案5】:类既可以是公开的也可以是密封的。
但是,不要那样做。
您可以创建一个工具来反射内部类,并发出一个通过反射访问所有内容的新类。 MSTest 会这样做。
编辑:我的意思是,如果您不想在原始程序集中包含任何测试内容;如果成员是私有的,这也有效。
【讨论】:
等等,什么?你是说不要public sealed class
?你对那颗宝石的理由是什么?
@crush trampony 自 2009 年以来未登录。以上是关于使代码内部化,但可用于其他项目的单元测试的主要内容,如果未能解决你的问题,请参考以下文章