使代码内部化,但可用于其他项目的单元测试


129

我们将所有单元测试放在他们自己的项目中。我们发现,对于单元测试,我们必须将某些类公开而不是内部类。无论如何要避免这样做。通过公开类而不是密封类,对内存有什么影响?


Answers:


205

如果使用的是.NET,则InternalsVisibleTo程序集属性允许您创建“朋友”程序集。这些是特定的强命名程序集,允许它们访问内部类和另一个程序集的成员。

注意,应谨慎使用它,因为它会紧密耦合所涉及的程序集。InternalsVisibleTo的常见用法是用于单元测试项目。由于上述原因,它可能不是在实际应用程序集中使用的好选择。

例:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{

23
我建议在属性周围放置#if DEBUG,然后在调试中进行单元测试。这样,您将确保未在发布代码上设置该属性。
史蒂夫·库克

这只是个主意,我不知道。...怎么样:#if DEBUG公共类IniReader #else内部类IniReader #endif可能不推荐?为什么?
jmelhus

4
好吧,为什么将测试限制为调试版本?
Marco Mp

2
另外,只是一个小问题,不需要对“朋友”程序集进行强命名(这可能是个好习惯-即使我的个人喜好相反,但不是强制性的)。
Marco Mp

9
不同意。如果我要使用非常薄的公共API构建复杂的组件,则仅通过公共API进行测试是不切实际和不切实际的。您最终将遇到无法维护的泥球。相反,我会仔细定义内部单元并分别进行测试。正如杰里米·米勒(Jeremy D. Miller)经常说的:“先测试小,然后再测试大”。
Dennis Doomen,2015年

6

如果它是内部类,则不能孤立地使用它。因此,除了测试内部使用该对象的其他类之外,您实际上不应该对其进行测试。

就像您不应该测试类的私有成员一样,您也不应该测试DLL的内部类。这些类是一些公共可访问类的实现细节,因此应通过其他单元测试很好地进行练习。

这个想法是您只想测试一个类的行为,因为如果您测试内部实现的细节,那么您的测试将很脆弱。您应该能够在不破坏所有测试的情况下更改任何类的实现细节。

如果发现确实需要测试该类,那么您可能首先要重新检查该类是内部类的原因。


2
实施细节应作为全面测试的一部分。不要偷看私有变量...测试预期的行为。如果测试正确,则应测试所有内部管道和接线。投票了。
Gishu

69
我不一定同意这一点,因为这些类对于DLL中的其他类是“公共的”,并且该类的功能应进行独立测试
leora

27
我也不同意。单位是单位,必须对其进行隔离测试。
Sentinel

5
伟大的一般准则,但让我们不要教条。测试有两个主要目的:1)回归-确保您没有破坏某些东西,2)提高开发速度。如果每次我想编写一行代码时都必须站起来提供大量服务,那么我将阻碍开发。如果我有一个复杂的内部组件,我希望能够独立进行开发和测试。相反,我不希望所有事物都进行隔离测试(因此降低回归测试值或复制测试代码),但是有些代码证明隔离是合理的。也许关键是使其成为单独的组件。
李·詹森

2
OP在这里是正确的。您正在将测试耦合到内部实现。因此,您的团队永远是修补测试和可怕的模拟代码的奴隶。仅测试公共API,无论是打包的lib API还是网络公开的API。所有代码应可通过前门执行。测试实现是TDD在很大程度上消亡的原因,它实际上是测试整个应用程序的每个类的巨大PITA,而不是专注于确保系统行为。我认为几乎每个人,包括“权威”书籍,都犯了这个错误或没有弄清楚。
路加·普普利特

4

出于文档目的

或者,您可以使用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*/});

-5

类可以是公开的也可以是密封的。

但是,不要那样做。

您可以创建一个工具来反映内部类,并发出一个通过反射访问所有内容的新类。MSTest做到了。

编辑:我的意思是,如果您不想在原始程序集中包含-any-测试内容;如果成员是私人的,这也适用。


1
等一下 你是说不要做public sealed class?你对那颗宝石有什么看法?
暗恋

自2009
Falken教授
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.