你如何对依赖于许多其他类的类进行单元测试?

时间:2008-12-06 14:57:11

标签: unit-testing

我听说通过单元测试我们可以捕获代码中的大部分错误,我真的相信这是真的。但我的问题是在大型项目中,每个类依赖于许多其他类,你如何进行单元测试类?由于编写存根所需的复杂性和时间,所以对其他所有类进行存根没有多大意义。你对此有何看法?

9 个答案:

答案 0 :(得分:12)

使用测试的部分优势在于它会强制您最小化依赖关系,从而创建可测试的代码。通过最小化依赖关系,您将提高代码的可维护性和可重用性,这两者都是非常理想的特性。

由于您将测试引入现有的代码库,您无疑会遇到许多难以测试的情况,这些情况需要重构才能正确测试。这种重构将增加代码的可测试性,同时减少依赖性。

之所以难以通过测试改进代码是为什么许多人主张遵循测试驱动开发。如果您先编写测试然后编写代码以通过测试,那么默认情况下,您的代码将更加可测试和解耦。

答案 1 :(得分:4)

使用模拟框架为您存储类。模拟框架(我使用Rhino Mocks for C#/ .NET)可以很容易地删除你的依赖项。与依赖注入一起使用时,您的类可以很容易地与其他类分离,并且不需要花费太多工作来实现它们。请注意,有时它会变得更容易“伪造”某些东西而不是模仿。我最终伪造了几个类,但通常这些很容易编写 - 它们只包含“好”或“坏”数据并返回它。没有涉及逻辑。

答案 2 :(得分:0)

我不能在整个单元测试中加速,但我认为每个单元都应该代表某种测试。测试用例应该测试一些程序。程序可能不限于单个班级。

例如,如果您的应用程序可以分解为原子组件,那么每个组件和组件之间每个适用的转换链都可能存在测试用例。

答案 3 :(得分:0)

在我们的项目中,开发人员有责任从一开始就编写和维护存根。

尽管存根需要时间和金钱,但单元测试提供了一些不可否认的优势(并且您也同意了)。它允许测试过程的自动化,减少发现应用程序中更复杂部分中包含的错误的难度,并且通常会增强测试覆盖率,因为每个单元都得到了关注。

根据我的经验,我已经看到,在我们将单元测试置于低优先级的项目中,必须在稍后阶段遭受损失,单个更改要么会破坏,要么需要额外的测试。

单元测试增加了我作为软件开发人员的信心。

答案 4 :(得分:0)

我更喜欢单元测试功能,可能会或可能不会对应于各个类别。在我看来,这种单元测试的粒度在测试所需的工作,客户的反馈(因为功能是他们付出的代价)以及单元测试的长期效用(体现要求)方面是更好的权衡。 ,说明操作方法)

因此,很少需要进行模拟,但有些测试跨越多个类

答案 5 :(得分:0)

  

但我的问题是大型项目   每个班级都依赖于许多班级   其他课程你怎么去单位   测试班级?

首先,你明白“每个班级依赖于许多其他班级”是件坏事,对吧?而且这不是一个大项目的功能,而是一个糟糕的设计? (这是TDD的一个好处,它倾向于阻止这种高度耦合的代码。)

  

其他所有课程都没有   因为两者都有意义   复杂性和所需的时间   写下存根。

更不用说它对设计问题没有帮助。更糟糕的是,对所有存根的投资实际上可能是重构的障碍,如果只是心理上的那么。

更好的方法(恕我直言)是从内到外开始,随时改变设计。通常我会通过做我称之为“内部依赖注入”的方式来解决这个问题。这涉及保持方法签名不变,但从依赖关系中提取所需的数据,然后提取保存实际逻辑的函数。一个简单的例子可能是:

public int foo(A a, B b) {
  C c = a.getC();
  D d = b.getD();

  Bar bar = calculateBar(c, d);

  return calculateFoo(bar, this.baz);
}

Bar calculateBar(C c, D d) {
  ...
}

int calculateFoo(Bar bar, Baz baz) {
  ...
}

现在我可以为calculateBar和calculateBaz编写测试,但我不需要担心设置内部状态(this.baz)而且我不需要担心创建A和B的模拟版本。 / p>

在使用现有接口创建此类测试之后,我寻找机会将更改推送到界面,例如更改Foo方法以获取C和D而不是A和B.

这有什么意义吗?

答案 6 :(得分:0)

你是对的,将你正在测试的课所依赖的所有课程都打包不值得。如果你改变接口,你也必须维护你的模拟。

如果您正在测试的类已经过测试,那么测试跨越几个类就可以了。

模拟对象有时更简单:

  • 对象是或使用外部资源,如数据库或网络连接,磁盘
  • object是一个GUI
  • 对象尚未[可用]
  • 对象行为不确定
  • 设置对象的费用很高

答案 7 :(得分:0)

我觉得您可能需要重构以降低复杂性。

我发现重构类(即分解它)非常有用,因此每个人都有一个责任(不那么复杂)。然后你可以单独测试这个责任。您可以使用模拟框架轻松模拟和初始化依赖项。我发现FakeItEasy非常好且易于使用,你可以找到很好的例子

答案 8 :(得分:0)

以下是适用于可测试性的原则,也适用于大型企业项目中的稳健设计:

  1. 通过类构造函数使它们显式化。

  2. 不是将具体类作为依赖项,而是创建定义所需功能的接口,并将这些接口作为类构造函数的依赖项。这也允许您在需要时应用依赖注入模式。

  3. 使用Moq等现有框架之一,这样您就不需要编写接口的完整测试实现,但在运行时从单元测试中创建Moq对象。