单元测试应仅涵盖“功能性”软件


9

我们正在一个新的软件开发项目中使用StructureMap。团队成员之一已实现了一个单元测试,该单元测试基本上测试了StructureMap容器配置。它通过执行以下操作来做到这一点;

  • 计算为应用程序名称空间中的类配置的程序集的实例数。
  • 在类级别定义期望的实例
  • 断言预期实例与找到的实例总数匹配。
  • 断言预期实例与测试中定义的实例匹配

一个例子是;

var repositories = container.GetAllInstances<IEnvironmentRepository>();
Assert.AreEqual(1, repositories .Count());
foundInstances = foundInstances + repositories .Count();

我们还为下一节课提供“单元测试”;

public MyClass(IEnvironmentRepository environmentRepository)
        {

        }

在这些测试中,我们模拟了IEnvironmentRepository,因此不会像在实时系统中那样从容器中注入它。

一位同事忽略了对结构图配置的单元测试,并带有“单元测试仅测试它自己的配置”的注释。这显然是测试的目的,我认为这是完全正确的。我要求忽略测试的人删除结构映射配置IEnvironmentRepository(仍然忽略测试)并运行完整的单元测试套件,它们都通过了。然后,我们运行了该应用程序,由于容器配置现在无效,因此该应用程序崩溃了。我认为,这证明了测试的价值,我的同事仍然不同意。他只是简单地说我们不应该测试配置,但是我认为这完全可以进行单元测试。

有很多问题;

  • 这是有效的单元测试吗?-我们正在测试容器的配置,而不是结构图起作用(但我可以看到重叠的部分)
  • 如果不是,您如何在不测试的情况下验证配置。您如何阻止某人意外删除所需的代码行并将其检入?
  • MyClass单元测试是否应该IEnvironmentRepository从容器中解析出实例并传递给它?

10
在测试的10个分歧中,有9个来自以下事实:框架支持所有形式的自动化测试,人们希望了解特定自动化测试是否是好的和正确的单元测试的语义。您描述的测试听起来像是一种非相当的单元测试,对于拥有和自动化(并在签入中运行)可能非常有用-只是不要将其称为单元测试。询问您的同事是否将测试放在单独的功能/文件夹中,如果晚上睡得更好。
Jeroen Mostert

2
这也是我的观点,可能有用,尽管严格地说不是单元测试,但它确实增加了价值,并且已经证明了这一点。他的回答是其他单元测试也可以解决这个问题,但是在我看来,如果将它们编写为严格的单元测试,您将被嘲笑依赖项,因此在您使用它之前,永远不会知道配置是否有效。
克里斯宾特(ChrisBint)

4
您的同事有一个要点,当他说不测试配置时,因为不能/不应该测试因部署而实际变化的真实配置-谁说“红色”是错误的而“蓝色”不是?该测试将与一种设置紧密结合。但是与代码工件相关的配置是一个例外,因为它没有变化,并且显然有错误的解决方法。理想情况下,您将在构建时从DRY元数据生成这样的配置,但是在这种情况下,像这样的测试确实会增加价值。比避免可避免的部署错误更好。
Jeroen Mostert

2
您所描述的内容不是测试单元,而是测试第三方软件的配置。拥有测试这些东西的测试非常有用,但是它们是集成测试,而不是单元测试,而断开连接可能是分歧的根源。
Phoshi

3
@ChrisBint天哪,我自己写了一堆容器测试。它们具有很大的价值,它们不是单元测试。很好,集成测试对于捕获单元测试无法做到的事情非常有价值。
Phoshi

Answers:


13

这是一个完全有效的自动化测试。我称它们为“架构测试”,是因为它们可以验证代码库的骨骼组件的健全性。

IoC容器是否能够解析和组合应用程序中的所有对象树?自动Mapper可以在所有已注册对象之间进行映射而不会失败吗?洋葱架构中的中央层是否引用任何外部内容?

通过指出确切的罪魁祸首,这些测试可以在配置错误发生时为您节省大量时间。好的框架会为您提供关于错误原因的非常精确的错误消息,并且您可以在运行测试后(理想情况下是连续地)立即获得它们,而不必走运,如果幸运的话,也不必深入了解运行时堆栈跟踪。

无论它们是否是单元测试……也许不是,但是它们仍然在内存中运行大部分并且运行非常快。再说一次,我不知道,这不像有一个普遍接受的单元测试定义。


具有讽刺意味的是,这几乎就是我向同事解释的方式,即使进行了验证(删除了一个容器实例并运行应用程序),他仍然看不到任何价值。我了解每个人都有自己的见解,我说了我的意见;)我喜欢“架构测试”一词,我将窃取它!
克里斯宾特

6

像这样的测试的问题,它测试程序的内部而不是要求。即使程序按要求工作,测试是否也会失败?

在您的情况下,每当更改容器设置时,也许您有一个需要注入的新依赖项,就会破坏测试。

此外,如果您添加了额外的依赖项要求,但忘记将其添加到容器中并更改了容器测试。一切都会过去,但是您的程序将崩溃。

更好的自动化测试是启动程序,看看它是否崩溃。

您应该在集成或UI测试中捕获这些类型的错误,即使它们属于单元测试范围。

话虽这么说,容器设置的复杂性越来越高,这是一个麻烦。也许一些“不好的”测试是值得的。


1

单元测试测试代码。除此之外,还有“其他”自动化测试-随便怎么说。您似乎在这里测试配置。如果配置可以根据环境而更改,则它不属于单元测试。考虑添加一个测试属性以指示该测试与其他测试的类型不同。


配置是静态的,它不是由环境驱动的,配置中存在的所有类将以相同的方式在所有环境中使用。是的,配置中可能包含的实例数量应与配置中的实例数量相匹配,这测试的一部分。如我的示例所示,删除IEnvironmentRepository允许其他单元测试通过。特定的容器测试将在2个Asserts上失败;1-可能的实例声明总数不匹配,2-IEnvironmentRepository的实例的具体数量不匹配。
克里斯宾特

1
容器的正确性由编码器定义。对于每次修改,被测代码和测试本身都必须更改的事实立即使警报铃声响起。DI是达到目的的手段,而不是目的本身。完全可以用DI样式编写代码而无需使用结构映射,这在我看来不是真正的单元测试。当然需要对容器进行验证,但是由于此处提供的信息有限,因此使用自动测试进行验证的有效性似乎有些微不足道。
罗比·迪

2
单元测试需要10分钟才能完成。部署可能需要一个多小时。
克里斯宾特

1
给定单元测试的一部分,可以具体验证配置中是否存在单行,不确定该如何隔离。我可以同意的一般数字。
克里斯宾特

1
那时他们可能会有一些里程-确实需要您作出判断。但是它们应该在物理上或通过某种属性分开。
罗比·迪

0

依赖注入容器的职责是在一个工作应用程序中粘贴不同的模块

如果您为应用程序编写自动化测试-您应该只有很少的“集成(或验收)测试”可以“端到端”执行测试,这些测试将测试应用程序的整个管道,从而将特定测试用例中涉及的所有模块粘合在一起正确的

因此,如果未正确配置依赖项注入容器,则这些集成测试将失败。这会使容器本身的单元测试无用,因为集成测试应该显示容器配置中可能的错误。

您不需要在集成测试中涵盖所有可能的测试用例,每个功能只需一个测试用例即可涵盖从UI到数据库的完整方法。

如果集成测试用例不包括某些特定依赖的实例化,则只需添加这样的实例。

使用集成测试,您可以自由更改容器,而无需为它们的配置重写单元测试。


0

IMO,答案是:

  1. 这是有效的单元测试吗?-我们正在测试容器的配置,而不是结构图起作用(但我可以看到重叠的部分)

    • 它是针对structuremap的有效单元测试,而不是针对您的项目的有效单元测试,因为单一测试会测试一些特定的代码,并在必要时模拟所有依赖项以测试实现的逻辑。配置逻辑是在structuremap内实现的,因此该库必须经过良好的测试,并且必须包含您所提到的单元测试,以及更多内容:它应该包含数百个这样的测试,在运行时动态模拟多个配置并进行测试以查看是否容器行为正常。
  2. 如果不是,您如何在不测试的情况下验证配置。您如何阻止某人意外删除所需的代码行并将其检入?

    • 您可以在所需的环境中手动测试配置,也可以为此创建一个自动化(自动化测试),以测试所需的特定配置(无需在运行时模拟内容)。
  3. MyClass单元测试是否应该从容器中解析IEnvironmentRepository的实例并将其传递给它?

    • 不,这是一个完美的单元测试,因为您可以模拟依赖关系并以隔离的方式测试MyClass逻辑。

-1

UnitTest验证分离中某个单元的期望行为

这是指任何类型的配置不是在范围单元测试

尽管如此,您应该对配置进行自动化测试,但这不是UnitTests ...


您从哪里获得单位的定义?
克里斯宾特

我喜欢“ 单元测试的艺术”中Roy Osherove之一:单元是具有相同更改理由的(生产)代码块。我的世界通常从单班到三到五个……
蒂莫西·卡特尔

这是正在测试的生产代码。
克里斯宾特

只是想引开nitpickers,没想到它的工作原理周围的其他方式太...; O)
蒂莫西脚轮
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.