你如何进行单元测试?

时间:2008-11-28 18:06:04

标签: unit-testing

我已经阅读了一些关于单元测试的内容,并且想知道你是如何进行单元测试的。显然,单元测试应该将程序分解为非常小的“单元”并从那里测试功能。

但我想知道,单元测试课程是否足够?或者你是否进一步采用单元测试算法,公式等?或者你把它扩展到单元测试asp页面/功能?或者你进行单元测试?

9 个答案:

答案 0 :(得分:5)

  

但我想知道,单元测试课程是否足够?或者你是否进一步采用单元测试算法,公式等?或者你把它扩展到单元测试asp页面/功能?或者你进行单元测试?

算法应该在一个类中,并且应该自动进行单元测试。一个公式作为一个函数在类中,它们也是单元测试。单元测试测试行为,状态以及可以针对最小开发单元进行测试的所有事情。所以,是的,算法的所有细节都经过测试。稍后,当您拥有使用其他类的类时,您将执行集成测试(这些测试通常使用单元测试程序进行测试)。这将是相同的,但在更高的层次。

答案 1 :(得分:5)

在对代码进行一些更改后(例如,重构,修复错误,添加增强功能),我使用单元测试作为衡量某些内容是否仍然有效的工具。 由于我使用Java,因此单元测试在很大程度上使用JUnit进行自动化。我只调用一个命令行脚本,它运行数百个测试来验证代码是否已损坏。

答案 2 :(得分:2)

我单元测试功能,而不是单个方法或类。编写和维护单元测试的开销并不是微不足道的,一般来说,我不建议为每一小段代码编写单元测试。但是,单元测试功能是值得的,因为功能是客户为您付出的代价。

答案 3 :(得分:2)

这些是我认为对单元测试有用的通用指南:

1)识别边界对象(Win / WebForms,CustomControls等)。

2)识别控制对象(业务层对象)

3)确保为边界对象调用的控制对象公共方法写单元测试至少

通过这种方式,您可以确保覆盖应用程序的主要功能方面(功能),并且不会冒微测试的风险(除非您想要)。

答案 4 :(得分:2)

我是一个非常邋unit的单位测试员,但后来我是一名学者,所以我的大多数程序都是我自己使用的脚本或者是我研究的更重要的程序。我的很多工作都是关于编译器的,所以经典的单元测试很难 - 例如,除非你是一个编译器来包装它,否则单元测试寄存器分配器并不容易,此时你也可以去回归测试。

但是有一些很大的例外。我在Lua中做了大量的脚本编写,它分为模块(源文件可以并且通常是模块)。如果我正在处理新的东西,比如让我们说实用程序与shell进行交互,我只会在模块内部放弃一些单元测试。其中每次加载模块时都会运行。 Lua是如此之快,通常这无关紧要。以下是一些例子:

assert(os.quote [[three]] == [[three]])
assert(os.quote [[three"]] == [['three"']])
assert(os.quote [[your mama]] == [['your mama']])
assert(os.quote [[$i]] == [['$i']])

如果我是一只好狗,我会在写这个函数之前写一些像这样的简单测试。

我对单元测试做的另一件事是,如果有什么困难,我使用QuickCheck测试代数定律,这是一个必须被视为相信的随机测试工具。它是我用过的唯一一个让单元测试有趣的工具。有一个悬挂的链接,但你可以在他的博客上找到Tom Moertel's story about the ICFP programming contest

希望您觉得这很有帮助。 QuickCheck多次保存我的培根。最近,我使用精确的有理算法测试了离散余弦变换的代码 - 然后将其移植到C!

答案 5 :(得分:1)

我们通常在Java库中为机器编程。一个程序通常由二十多个库组成,所以我们所做的就是对每个库进行单元测试。这不是一件容易的事,因为很多时候库彼此之间非常耦合,这不是很多次。

我们的代码并不像我们希望的那样模块化,但我们必须忍受它的兼容性问题,并且断开耦合意味着在许多情况下也会破坏兼容性。

答案 6 :(得分:1)

我尽可能多地测试公共接口(我使用的是C ++,但语言并不重要)。最重要的方面是在编写代码时(紧接在之前或之后)编写测试。根据经验,我向您保证,以这种方式开发将带来更可靠的代码。并且会使维护更容易(因为显然会立即破坏测试的变化)。

对于所有项目,我建议你从一开始就考虑测试 - 如果你编写一个依赖于另一个复杂类的类,那么使用一个接口,这样你就可以在测试时“模拟”更复杂的对象(数据库访问,网络访问等。)。

编写大量测试似乎会让您失望,但实际上,在项目的整个生命周期中,您将花费更少的时间来修复错误。

经常测试 - 如果它可以破坏,它会 - 并且在测试它时比在客户尝试使用它时更好。

答案 7 :(得分:1)

单独测试课程是不够的。课程一起工作,也必须进行测试。

单元不仅仅是类:

  • modules,
  • layers,
  • 框架。

当然有不同形式的测试,例如整合和接受。

我测试了我认为困难的事情,我认为可能会改变的事情,接口以及我必须解决的问题。我主要是从测试开始,试图确保我理解我想要解决的问题。

答案 8 :(得分:1)

仅仅因为它编译并不意味着它运行!这是单元测试的本质。尝试代码。确保它正在按照您的想法进行操作。

让我们面对它,如果你从matlab带来一个矩阵变换,很容易在某个地方弄乱一个加号或减号。这种事情很难看出来。没有尝试,你只是不知道它是否会正常工作。 调试100行代码要比调试100,000行代码容易得多。


有些人认为这是极端的。他们试图测试每一个可以想象的东西。测试成为自己的目的。

稍后在维护阶段可能会有用。您可以快速检查以确保您的更新没有破坏任何内容。

但所涉及的开销可能削弱产品开发!未来改变功能的更改可能涉及大量的测试更新开销。

(对于多线程和任意执行顺序,它也会变得混乱。)


最终,除非另有指示,否则我的测试会试图找到中间地带。

我希望以更大的粒度进行测试,提供验证基本常规功能的方法。我并不担心每一个可能的fencepost场景。 (这就是ASSERT宏的用途。)

例如:当我编写代码以通过UDP发送/接收消息时,我将快速测试汇总到使用该类通过环回接口发送/接收数据。没有什么花哨。快速,快速,和脏代码。我只是想尝试一下。为了确保它在我构建它之前实际工作。

另一个例子:从Firewire相机中读取相机图像。我把一个快速和肮脏的GTK应用程序放在一起来读取图像,处理它们,并实时显示它们。其他人称之为集成测试。但我可以用它来验证我的Firewire界面,我的Image类,我的Bayer RGGB-> RGB变换,我的图像方向&对齐,即使相机是否颠倒安装。只有在证明不足的情况下才能进行更详细的测试。

另一方面,即使对于简单的事情:

template<class TYPE> inline TYPE MIN(const TYPE & x, const TYPE & y) { return x > y ? y : x; }
template<class TYPE> inline TYPE MAX(const TYPE & x, const TYPE & y) { return x < y ? y : x; }

我写了一行SHOW宏来确保我没有弄乱这个标志:

  SHOW(MIN(3,4));  SHOW(MAX(3,4));

我想做的就是验证它在一般情况下正在做它应该做的事情。我担心它是如何处理NaN / + -Infinity /(double,int)的,而不是一个同事决定改变参数顺序和傻瓜。


工具方面,那里有很多单元测试的东西。如果它对你有所帮助,对你有更多的力量。如果没有,那么你真的不需要过于花哨。

我经常编写一个测试程序,将数据转入和转出类,然后用SHOW宏打印出来:

#define SHOW(X)  std::cout << # X " = " << (X) << std::endl

(或者,我的许多类都可以使用内置运算符&lt;&lt;(ostream&amp;)方法进行自我打印。这是一种非常有用的调试和测试技术!)

Makefile可以简单地扩展为自动生成测试程序的输出文件,并自动比较(差异)这些输出文件与以前已知(已审查)的结果。

不花哨,也许不如优雅,但随着技术的发展,这非常有效,实施起来快,而且开销很低。 (当你的经理不赞成在测试中浪费时间时,这有其优势。)


最后一个想法我会离开你。 这会让我被标记下来,所以不要这样做!

前段时间我需要一个测试程序。这是必需的可交付成果。该程序本身必须验证另一个类是否正常工作。但它无法访问外部数据文件。 (我们无法依赖程序相对于其他任何位置的位置。也没有绝对路径。)项目的单元测试框架与我需要使用的编译器不兼容。它也必须在一个文件中。项目makefile系统不支持将多个文件链接在一起以进行低级测试程序。 (应用程序,当然。他们可以使用库。但每个测试程序只有一个文件。)

所以,上帝原谅我,我“打破了规则” ......

&LT;尴尬&GT;
我用过宏。设置#define宏后,数据将写入第二个.c文件,作为结构数组的初始化程序。随后,当重新编​​译软件时,第二个.c文件(带有struct数组)是#included,并且未设置#define宏,它将新结果与先前存储的数据进行比较。是的,我#included一个.c文件。哦,这一切的尴尬。
&LT; /尴尬&GT;

但是可以做到......