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


137

为了方便起见,您是否将单元测试放在同一项目中?还是将它们放在单独的程序集中?

如果像我们一样将它们放在单独的程序集中,则最终在解决方案中会有许多其他项目。这非常适合编码时的单元测试,但是如何在没有所有这些额外程序集的情况下发布应用程序?

Answers:


100

我认为,单元测试应与生产代码放在单独的程序集中。这只是将单元测试与生产代码放在相同的一个或多个组件中的一些缺点:

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

我真的不了解任何优点。拥有一个额外的项目(或10个项目)不是坏事。

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

我还建议任何自动化的构建过程都将生产和单元测试放在不同的位置。理想情况下,仅当生成生产代码并将产品文件复制到单元测试目录中时,才运行单元测试构建过程。以这种方式进行操作会导致实际的部分被分离以便装运,等等。此外,此时在特定目录中的所有测试上运行自动化的单元测试非常简单。

总而言之,这是每日构建,测试和运送钻头和其他文件的一般思路:

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

15
IMO您列出的缺点并不总是适用。同一项目的专业人士更容易将已测试的类+测试进行分组-这些小的便利在编写测试时大有帮助。个人喜好在这里胜出,有时您的观点很重要,但并非始终如此。
orip

7
您应该首先询问在运送时是否需要删除测试。如果是,则需要一个单独的项目。如果没有,请使用其他利弊来决定。假定他们无法部署测试的人们将在默认情况下始终得出“单独的项目”结论。
orip

7
我认为交付非生产代码(如单元测试)没有任何弊端,并且存在很多弊端。运输单元测试意味着您正在处理需要分配的更多位。单元测试还具有一组独立的依赖项。现在,您正在运送NUnit,Rhino或Moq等。这甚至有些膨胀。将单元测试放置到一个单独的项目中只需要少量的工作,这是一次性的成本。我对不应该进行单元测试的结论感到非常满意。
杰森·杰克逊

4
作为在生产代码中删除单元测试的“单独项目”方法的替代方法,请考虑使用自定义符号和类似的编译器指令#if。可以使用CI软件脚本中的编译器命令行参数来切换此自定义符号。请参阅msdn.microsoft.com/en-us/library/4y6tbswk.aspx
Rich C

23
.NET在完全独立的项目中进行测试是落后的,我敢打赌这很快就会改变。没有理由不能使构建过程忽略发行版本中的测试代码。我已经使用了几个执行此操作的Web应用程序生成器。我认为最好将单元测试代码文件与它们描述的代码文件一起包括在整个相同文件层次结构中。Google推荐这种方式,称为“分形”文件组织。为什么测试与内联注释和自述文档有什么不同?编译器很乐意忽略后者。
布兰登·阿诺德

112

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

其他人陈述了单独项目的原因。请注意,如果您使用的是数据驱动的测试,那么如果您将测试包含在生产程序集中,则可能会导致大量的膨胀。

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


+1:我刚刚在与主代码相同的项目中遇到了单元测试,即使遵循命名约定,也很难在真实代码中找到测试。
Fenton'9

32
对于经验不足的读者,这意味着您将添加[assembly:InternalsVisibleTo("UnitTestProjectName")]到项目AssemblyInfo.cs文件中。
Margus

Margus非常有用。感谢Jon在这种情况下提到InternalsVisibleTo。
比昂·奥托·瓦斯博滕(BjørnOtto Vasbotten)2014年

1
@jropella:如果要对生产程序集进行签名,则无论如何都只能使内部对签名的程序集可见。当然,如果您完全信任运行任何代码,它们都可以通过反射来访问...
Jon Skeet 2015年

2
@jropella:反射不关心InternalsVisibleTo反正...但你不会看到一个签署组装信任的签名使用InternalsVisibleTo总成,因为这是防止在编译时。
乔恩·斯基特

70

我不理解经常使用生产代码部署测试的异议。我领导了一个小型微型团队(由14人增加到130人)组成的团队。我们有六个左右的Java应用程序,我们发现将测试部署到现场以在特定位置执行它们非常有价值机器表现出异常行为。现场发生了随机问题,能够以零成本进行神秘的数千次单元测试是无价的,而且通常可以在几分钟内诊断出问题……包括安装问题,不稳定的RAM问题,特定于机器的问题,不稳定的网络问题,等等。我认为将测试投入领域非常有价值。此外,随机问题会在随机时间弹出,很高兴让单元测试坐在那里已经等待一时通知。硬盘空间很便宜。就像我们试图将数据和功能保持在一起(OO设计)一样,我认为将代码和测试保持在一起(功能+验证功能的测试)从根本上是有价值的。

我想将测试放在C#/。NET / Visual Studio 2008中的同一项目中,但是我仍然没有进行足够的研究来实现它。

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


17
我可以看到这对于集成测试有什么价值,但是对于单元测试呢?如果正确编写它们,则它们在计算机上不应有任何依赖关系。
Joel McBeth 2012年

我同意+1。由于我主要使用.NET进行操作,因此使用生产代码进行运输测试还具有减少编译和测试的持续集成步骤的良好副作用:1)仅构建一半项目,2)所有测试程序集代替主应用程序因此更容易参数化您的测试运行器。当然,使用Maven时,此参数不成立。最后,我的客户是技术含量更高的客户,他们非常希望能够自己运行测试,因为这是在根据规范记录当前状态。
mkoertgen 2013年

我认为以TDD / BDD风格进行集成测试非常有意义,也就是说,编写可以使用标准测试运行程序(例如NUnit,xUnit等)轻松实现的测试代码。当然,您不应该将单元与集成测试混为一谈。只要确保您知道哪些测试集可以快速且经常地运行以进行构建验证(BVT),以及哪些测试集可以进行集成测试。
mkoertgen

1
人们反对它的原因是因为这不是他们习惯的。如果他们的第一个单元测试经验是他们在生产代码中,实际上,他们使用编程语言中的特定语法来表明测试与生产方法相关联,那么他们会发誓这是开发工作流程的一个宝贵方面。将它们放在一个单独的项目中绝对是疯狂的。大多数开发者都是这样。追随者。
zumalifeguard 2014年

19

将单元测试与代码放在同一项目中,以实现更好的封装。

您可以轻松地测试内部方法,这意味着您不会公开应该是内部的方法。

使单元测试接近您正在编写的代码也非常好。编写方法时,因为它在同一个项目中,所以可以轻松找到相应的单元测试。当您构建包含unitTests的程序集时,unitTest中的任何错误都会给您带来编译器错误,因此您仅在构建时必须保持unittest为最新。在单独的项目中进行单元测试,可能会导致一些开发人员忘记构建unittest-project,并在一段时间内丢失了损坏的测试。

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

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


1
但是,这意味着您无法在内部Release版本上运行测试,并且很少会遇到“ heisenbug”。
2013年

3
通过朋友程序集(msdn.microsoft.com/en-us/library/0tke9fxk.aspx),可以使用InternalsVisibleTo单独的测试项目测试内部方法
ParoX

3
@hlpPy-您可以配置任意条件编译符号,并且可以具有两个以上的构建配置。例如; 你可能有DebugApprovalRelease; 并通过所有发行优化来编译批准。而且,通常,单元测试并不适合在一开始就检测海森氏病(以我的经验)。您可能需要针对它们的特定集成回归测试-并且您可以将它们并排放置。
Eamon Nerbonne '16

单独的项目给出相同的编译时错误。如果您需要深入了解代码,那么听起来更像是需要更多的抽象,而不是对代码进行单元测试。依靠代码中的条件条件来切换分支也不是一件好事。听起来更像是对单元和集成测试的困惑。
尼克·特纳

12

在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>

上面的内容确保了在Release配置中,所有命名的文件*.Tests.cs都不会从编译中排除,并且确保删除了必需的单元测试包引用。

如果您仍然希望能够在其发布配置中对这些类进行单元测试,则可以创建一个新的配置,该配置源自,ReleaseReleaseContainingTests


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

VS Code屏幕截图

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

"material-icon-theme.files.associations": {
  "*.Tests.cs": "test-jsx",
  "*.Mocks.cs": "merlin",
  "*.Interface.cs": "yaml",
}

正是我们刚刚开始做的事
马修·斯蒂夫斯

您是否也可以将其用于.net标准类库?
Zenuka

10

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


1
然后如何对内部类进行单元测试?还是只测试公共课程?
user19371

7
必要时<assembly:InternalsVisibleTo =“ TestProject” />,尽管我通常只测试公共接口。
tvanfosson

@tvanfosson,您如何处理Specs(Specflow)?您将有一个单独的项目,还是将它们放入单元测试项目中?+1。
w0051977年

@ w0051977我从未使用过Specflow,所以我不知道
tvanfosson

@tvanfosson,集成测试如何?您会将它们放在单元测试的单独项目中吗?
w0051977

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);
     }
}

此处的单元测试充当被测试代码的文档和规范。我不知道如何通过单独项目中的测试来实现这种自我记录的效果。函数的用户将不得不搜索测试以查看那些测试用例,这不太可能。

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


2
我喜欢这个主意;随代码一起提供了一些输入和预期输出的示例。
Preston McCormick

3

我在同一项目和不同项目之间波动。

如果您要发布的库会发布带有生产代码的测试代码是一个问题,否则我通常不会遇到问题(尽管尝试之前存在很大的心理障碍)。

将测试放在同一个项目中时,我发现在测试和它们测试的代码之间切换更加容易,并且重构/移动它们也更加容易。


3

我把它们放在单独的项目中。作为我们的一般规则,程序集的名称反映了名称空间的名称。因此,如果有一个名为Company.Product.Feature.sln的项目,则它具有Company.Product.Feature.dll的输出(程序集名称)。测试项目是Company.Product.Feature.Tests.sln,产生Company.Product.Feature.Tests.dll。

您最好将它们保留在单个解决方案中,并通过配置管理器控制输出。我们为每个主要分支(开发,集成,生产)都有一个命名配置,以代替使用默认的Debug和Release。配置设置完成后,您可以通过单击配置管理器中的“生成”复选框来包含或排除它们。(要获取配置管理器,请右键单击解决方案,然后转到配置管理器。)请注意,我发现Visual Studio中的CM有时有问题。有几次,我不得不进入项目和/或解决方案文件来清理它创建的目标。

另外,如果您使用的是Team Build(我确信其他.NET生成工具相同),则可以将生成与命名配置相关联。这意味着,例如,如果您不为“生产”构建构建单元测试,则构建项目也可以知道此设置,并且因为已将其标记为标准而无法构建它们。

另外,我们过去经常从构建机器上删除XCopy。该脚本将仅省略复制名为* .Tests.Dll的任何内容。这很简单,但是有效。


1

我会说把它们分开。

除了上面提到的其他原因之外,将代码和测试放在一起会使测试覆盖率数字倾斜。当您报告单元测试覆盖率时-报告的覆盖率更高,因为运行单元测试时覆盖了测试。当您报告集成测试覆盖率时,报告的覆盖率会降低,因为集成测试不会运行单元测试。


这不取决于用于测试的技术吗?我的意思是,OpenCover认为如果代码是由测试运行的,则包括测试本身的代码行,因此,如果测试具有意外的异常,它们也将被标记为未发现。
艾萨克·洛洛皮斯

0

我真的受Robert Lopez 的Flood NN库的单元测试框架的启发。它为每个单元测试类使用一个不同的项目,并拥有一个容纳所有这些项目的解决方案,以及一个编译并运行所有测试的主项目。

整洁的东西也是项目的布局。源文件位于一个文件夹中,但是VS项目的文件夹位于下面。这使您可以为不同的编译器创建不同的子文件夹。所有VS项目都附带了代码,因此任何人都可以轻松地运行任何或所有单元测试。


0

我知道这是一个非常老的问题,但是我想补充一下我的经验,最近我将单元测试的习惯从单独的项目更改为相同的项目。

为什么?

首先,我非常倾向于使主项目文件夹结构与测试项目相同。所以,如果我有一个文件Providers > DataProvider > SqlDataProvider.cs那么我将在单元测试项目中创建相同的结构,例如Providers > DataProvider > SqlDataProvider.Tests.cs

但是,在项目变得越来越大之后,一旦将文件从一个文件夹移动到另一个文件夹,或从一个项目移动到另一个项目,那么将这些文件与单元测试项目同步就变得非常麻烦。

其次,从要测试的课程导航到单元测试课程并非总是很容易。对于JavaScript和Python而言,这甚至更加困难。

最近,我开始练习,我创建的每个文件(例如 SqlDataProvider.cs)都在创建另一个带有Test后缀的文件,例如SqlDataProvider.Tests.cs

乍一看似乎会膨胀文件和库引用,但是从长远来看,您将乍一看消除移动文件综合症,并且您还可以确保,每个要测试的单个文件都会有一个配对文件。与 .Tests后缀。它使您轻松进入测试文件(因为它是并排的),而不是浏览单独的项目。

您甚至可以编写业务规则来扫描项目,并确定没有.Tests文件的类,并将其报告给所有者。您还可以轻松地告诉测试跑步者定位.Tests课程。

特别是对于Js和Python,您不需要从其他路径导入引用,只需使用要测试的目标文件的相同路径即可。

我使用这种做法已经有一段时间了,我认为在项目规模与可维护性以及对于项目新手的学习曲线之间的折衷是非常合理的。


我不同意。将项目分组然后再测试单个类要更有条理。如果您测试您的提供商;您将拥有一个IProvider,ISqlProvider和一个MockSqlConnection。在提供者文件夹中,您将为每个提供者进行单元测试。您可以深入进行单元测试,但是您只需编写测试而不是编写代码,甚至可以删除专业人士并开始编写代码进行测试。
尼克·特纳

0

正如其他人回答的那样- 将测试放在单独的项目中

尚未提及的一件事是,nunit3-console.exe除了.dll文件之外,您实际上无法对任何文件运行。

如果您打算通过TeamCity运行测试,则会带来问题。

假设您有一个控制台应用程序项目。编译时,它将可执行文件返回.exe到该bin文件夹。

nunit3-console.exe那里,您将无法运行在该控制台应用程序中定义的任何测试。

换句话说,控制台应用程序返回exe文件,而类库返回dll文件。

我今天有点被它吸引了:(


0

我最近一直在docker中进行部署。当Dockerfile可以轻松复制/ src目录并离开/ test目录时,我看不到拥有单独项目的价值。但是我是dotnet的新手,可能会缺少一些东西。


-2

单独的项目,尽管我与自己争论是否应该共享相同的svn。目前,我正在为他们提供单独的svn存储库,其中一个称为

“ MyProject”-用于项目本身

一个叫

“ MyProjectTests”-用于与MyProject关联的测试。

这是相当干净的,其优点是提交给项目和提交给测试是完全分开的。这也意味着您可以根据需要移交项目的svn,而不必发布测试。这也意味着您可以具有用于测试和项目的branch / trunk / tag目录。

但是,我越来越倾向于在每个项目的单个svn存储库中包含以下内容。

我的项目
| \ Trunk
| | \代码
| \测试
| \标签
| | \ 0.1
| | | \代码
| | \测试
| \ 0.2
| | \代码
| \测试
\分支
  \ MyFork
   | \代码
    \测试

我很想知道其他人对这个解决方案的看法。


5
您不需要为测试和应用程序代码进行单独的提交。他们应该齐头并进,并且提交会涉及到两者,尤其是如果使用* DD样式设计。
eddiegroves
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.