【问题标题】:Making code internal but available for unit testing from other projects使代码内部化,但可用于其他项目的单元测试
【发布时间】:2010-09-11 12:53:54
【问题描述】:

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

【问题讨论】:

标签: c# unit-testing scope


【解决方案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 应用程序中使用的方法。

    1. 添加AssemblyInfo.cs文件并添加[assembly: InternalsVisibleTo("AssemblytoVisible")]
    2. 将此添加到 .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 年以来未登录。
      猜你喜欢
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      • 2011-04-29
      • 1970-01-01
      • 2012-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多