您是否将单元测试放在同一个项目或其他项目中?

leo*_*ora 134 c# unit-testing

为方便起见,您是否将单元测试放在同一个项目中,或者将它们放在单独的组件中?

如果你像我们一样将它们放在一个单独的程序集中,我们最终会在解决方案中添加一些额外的项目.这对于编码时的单元测试很有用,但是如果没有所有这些额外的组件,你如何发布应用程序呢?

Jon*_*eet 108

单独的项目,但在同一解决方案.(我为产品提供了独立的测试和生产代码解决方案 - 这太可怕了.你总是在两者之间切换.)

单独项目的原因如其他人所述.请注意,如果您使用的是数据驱动的测试,那么如果将测试包含在生产装配中,则可能会导致相当大的膨胀.

如果需要访问生产代码的内部成员,请使用InternalsVisibleTo.

  • 对于经验较少的读者,这意味着你将`[assembly:InternalsVisibleTo("UnitTestProjectName")]`添加到你的项目`AssemblyInfo.cs`文件中. (26认同)
  • @jropella:无论如何,反射都不关心 InternalsVisibleTo ......但是你不会看到一个 *signed* 程序集信任使用 InternalsVisibleTo 的 *unsigned* 程序集,因为这是在编译时被阻止的。 (2认同)

Jas*_*son 95

在我看来,单元测试应该放在与生产代码不同的组件中.以下是将生产代码放在同一装配或组件中的单元测试的一些缺点:

  1. 单元测试随附生产代码.产品代码附带的唯一内容是生产代码.
  2. 单元测试会使组件不必要地膨胀.
  3. 单元测试可以影响构建过程,如自动或连续构建.

我真的不知道任何专业人士.有一个额外的项目(或10)不是一个骗局.

编辑:有关构建和运输的更多信息

我还建议任何自动构建过程将生产和单元测试放到不同的位置.理想情况下,单元测试构建过程仅在生成代码构建时运行,并将产品文件复制到单元测试目录中.这样做会导致实际的位被分开以便运输等.此外,在特定目录中的所有测试上运行自动单元测试是相当简单的.

总而言之,以下是每日构建和测试以及位和其他文件传输的一般概念:

  1. 生产构建运行,将生产文件放入特定的"生产"目录.
    1. 仅建立生产项目.
    2. 将编译的位和其他文件复制到"生产"目录中.
    3. 将位和其他文件复制到发布候选目录中,即圣诞节发布目录将是"Release20081225".
  2. 如果生成构建成功,则运行单元测试构建.
    1. 将生产代码复制到"tests"目录.
    2. 构建单元测试以"测试"目录.
    3. 运行单元测试.
  3. 将构建通知和单元测试结果发送给开发人员.
  4. 当一个候选版本(如Release20081225)被接受时,发送这些位.

  • .NET是在一个完全独立的项目中进行测试的曲线,我敢打赌,这很快就会改变.没有理由不能使构建过程忽略发布版本中的测试代码.我使用了几个这样做的Web应用程序生成器.我认为最好将单元测试代码文件与它们描述的代码文件一起包含在整个相同的文件层次结构中.谷歌推荐这个,称为'分形'文件组织.为什么测试与内联注释和自述文档有什么不同?编译器很乐意忽略后者. (18认同)
  • IMO您列出的缺点并不总是适用.同一个项目的专业人员更容易对测试类+测试进行分组 - 这些小的便利在编写测试时有很长的路要走.个人偏好在这里获胜,有时你的积分是相关的,而不是所有时间. (14认同)
  • 您应首先询问是否需要在发货时删除测试.如果是,您需要一个单独的项目.如果没有,请使用其他优点和缺点来决定.假设他们无法部署测试的人将始终默认达到"单独项目"的结论. (7认同)
  • 我没有看到像单元测试那样运输非生产代码的任何好处,并且有很多缺点.装运单元测试意味着您正在处理需要分发的更多位.单元测试还有一组独立的依赖项.现在你要发货NUnit,Rhino或Moq等等.这更加臃肿.将单元测试放入单独的项目只需要少量工作,这是一次性成本.我很满意单元测试不应该发货的结论. (7认同)
  • 作为在生产代码中删除单元测试的"单独项目"方法的替代方法,请考虑使用自定义符号和编译器指令,如`#if`.可以使用CI软件脚本中的编译器命令行参数切换此自定义符号.请参阅http://msdn.microsoft.com/en-us/library/4y6tbswk.aspx. (4认同)
  • 在不同项目中放置测试的一个非常大的缺点:测试距离很远,在到达 UnitTest 代码之前有很多点击。这增加了未完成/更新单元测试的机会。超过上面列出的 3 个缺点的 IMO。 (3认同)
  • 我已经回复了这个问题.尽管这个论点足够扎实,但下面的所有回复都指出了StackOverflow中的一个规则:以"我认为"中的单词"_"开头的回复_不是一个好的StackOverflow回复.一个好的回答就是".NET世界的一般规范将是......"或"你必须根据自己的情况做出决定,但......" (3认同)

小智 68

我不理解经常反对使用生产代码部署测试.我领导了一个小团队(从14人增加到130人).我们有六个左右的Java应用程序,我们发现它非常有价值,可以将测试部署到字段中以便在特定的机器表现出异常的行为.在现场发生随机问题,能够以零成本投入几千个单元测试是非常宝贵的,并且经常在几分钟内诊断出问题......包括安装问题,片状RAM问题,机器特定问题,网络问题,我认为将测试带入现场是非常有价值的.此外,随机时间会出现随机问题,让位于那里的单元测试等待立即执行是很好的.硬盘空间很便宜.就像我们试图将数据和功能保持在一起一样(OO设计),我认为在将代码和测试保持在一起(功能+测试验证功能)方面有一些根本性的价值.

我想在C#/ .NET/Visual Studio 2008中将我的测试放在同一个项目中,但我仍然没有调查过这个实现它的想法.

将Foo.cs保存在与FooTest.cs相同的项目中的一大好处是,当一个类缺少兄弟测试时,开发人员会不断被提醒!这鼓励了更好的测试驱动编码实践......漏洞更加明显.

  • 我可以看到集成测试对于单元测试有什么价值?如果你正确地写它们,它们不应该对机器有任何依赖性. (15认同)
  • 单元测试如何让您诊断片状RAM? (4认同)

Jam*_*ley 27

在 TypeScript 项目中花费了一些时间后,测试通常与他们正在测试的代码一起放在一个文件中,我越来越喜欢这种方法而不是将它们分开:

  • 导航到测试文件会更快。
  • 当您重命名被测试的类时,更容易记住重命名测试。
  • 移动正在测试的类时,更容易记住移动测试。
  • 一个类是否缺少测试是显而易见的。
  • 您不需要管理两个重复的文件结构,一个用于测试,一个用于代码。

因此,当我最近开始一个新的 .NET Core 项目时,我想看看是否有可能在 C# 项目中模拟这种结构,而无需在最终版本中发布测试或测试程序集。

到目前为止,将以下几行放在项目文件中似乎运行良好:

  <ItemGroup Condition="'$(Configuration)' == 'Release'">
    <Compile Remove="**\*.Tests.cs" />
  </ItemGroup>
  <ItemGroup Condition="'$(Configuration)' != 'Release'">
    <PackageReference Include="nunit" Version="3.11.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
  </ItemGroup>
Run Code Online (Sandbox Code Playgroud)

以上确保在Release配置中所有命名的文件*.Tests.cs都被排除在编译之外,并且还删除了所需的单元测试包引用。

如果您仍然希望能够在它们的发布配置中对类进行单元测试,您可以创建一个新的配置,从Release名为ReleaseContainingTests.


更新:使用此技术一段时间后,我还发现在 VS Code 中自定义图标很有帮助,以使测试(和其他内容)在资源管理器窗格中更加突出:

VS 代码截图

为此,请使用 Material Icon Theme 扩展并将以下内容添加到您的 VS Code 首选项 JSON:

"material-icon-theme.files.associations": {
  "*.Tests.cs": "test-jsx",
  "*.Mocks.cs": "merlin",
  "*.Interface.cs": "Raml",
}
Run Code Online (Sandbox Code Playgroud)

更新 2:

如果出现错误,Program has more than one entry point defined则可以按照此处的说明进行操作,这基本上<GenerateProgramFile>false</GenerateProgramFile><PropertyGroup>在项目.csproj文件中的元素内添加,这将阻止测试框架添加自己的入口点。

  • 这正是我们刚刚开始做的 (2认同)
  • @RoboKozo,您可以在项目文件中的“&lt;PropertyGroup&gt;”标记内添加“&lt;GenerateProgramFile&gt;false&lt;/GenerateProgramFile&gt;”。请参阅此处了解更多信息:https://andrewlock.net/fixing-the-error-program-has-more-than-one-entry-point-defined-for-console-apps-containing-xunit-tests/ (2认同)

jen*_*spo 17

将Unit测试放在与代码相同的项目中,以实现更好的封装.

您可以轻松地测试内部方法,这意味着您不会将方法设为公共内部方法.

让单元测试接近您正在编写的代码也是非常好的.编写方法时,您可以轻松找到相应的单元测试,因为它位于同一个项目中.当你构建一个包含unitTests的程序集时,unitTest中的任何错误都会给你一个compilereerror,所以你必须保持你的unittest是最新的,只是为了构建.在单独的项目中进行单元测试可能会导致一些开发人员忘记构建单元测试项目,并在一段时间内错过了破坏的测试.

您可以使用编译标签(I​​F #Debug)从生产代码中删除单元测试.

自动集成测试(由NUnit制作)应该在一个单独的项目中,因为它们不属于任何单个项目.

  • 对于朋友程序集(https://msdn.microsoft.com/en-us/library/0tke9fxk.aspx),使用 `InternalsVisibleTo` 允许您从单独的测试项目中测试内部方法 (4认同)
  • @hlpPy - 您可以配置任意条件编译符号,并且您可以拥有2个以上的构建配置.例如; 你可能有`Debug`,`Approval`和`Release`; 并使用所有版本优化编译批准.此外,通常单元测试在检测heisenbugs方面并不是很好(根据我的经验).您倾向于需要针对这些进行特定的集成回归测试 - 您可能会将这些测试放在一起. (3认同)

tva*_*son 10

我的单元测试总是在一个单独的项目中进行.事实上,对于我在我的解决方案中的每个项目,都有一个独立的测试项目.测试代码不是应用程序代码,不应与其混合.将它们保存在单独的项目中的一个优点 - 至少使用TestDriven.Net - 是我可以右键单击测试项目并运行该项目中的所有测试,只需单击一下即可测试整个应用程序代码库.

  • <assembly:InternalsVisibleTo ="TestProject"/>如果需要,虽然我通常只测试公共接口. (7认同)

And*_*nko 10

如果使用NUnit框架,则还有另外的理由将测试放在同一个项目中.请考虑以下与单元测试混合的生产代码示例:

public static class Ext
{
     [TestCase(1.1, Result = 1)]
     [TestCase(0.9, Result = 1)]
     public static int ToRoundedInt(this double d)
     {
         return (int) Math.Round(d);
     }
}
Run Code Online (Sandbox Code Playgroud)

此处的单元测试用作所测试代码的文档和规范.我不知道如何实现自我记录的这种效果,测试位于一个单独的项目中.该函数的用户必须搜索测试以查看这些测试用例,这是不太可能的.

更新:我知道这种TestCase属性的使用并不是NUnit的开发人员想要的,但为什么不呢?

  • 我喜欢这个主意;有几个输入和预期输出的例子,代码就在那里。 (2认同)