(一)单元测试实战:上路

发表于:2008-4-10 13:39

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Anders    来源:Anders .NET Space

分享:

        开始一个新的系列了。希望把单元测试的方方面面跟实际开发结合起来进行深入的了解。除了单元测试本身,还会涉及重构/修改代码、代码设计、TDD、自动化等方面的内容。好了,开始上路吧!

1、上路

1.1 什么是单元测试?

        单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常情况下,一个单元测试(用例)用于判断某个特定条件(或场景)下特定函数的行为。

        例如,我们要编写一个类,它要实现有序的List功能,当向其添加一个元素时,程序应该能够按照它的大小将其插到合适的位置。这是我们编写一个测试用例,将一个很大的值放入这个List中,然后确认该值出现在List的尾部。

        简单地说,执行单元测试是为了验证某段代码的行为是否与开发者所期望的一致。

单元测试是必要的吗?

        单元测试是由开发者编写和执行的,针对的也只是那些很小规模的代码,那么对于客户或最终用户来说,它们影响大吗?它们是必要的吗?

        其实,我们在进行单元测试时,我们所关心的规模很小的、独立的代码片段。我们的哲学是首先对所有单独部分的行为建立起信心(确信它们与期望一致),然后再安心的组装和测试整个系统。毕竟,如果盖房所用的砖瓦都没法保证质量,我们对房子还能期望什么呢?如果不先对砖瓦进行检查、验证,那么盲目的进行建筑岂不是极大的浪费?

        另一方面,单元测试只是各种测试中的第一步,在此之后,我们还需要其它形式的测试,比如集成测试、系统测试等,这个就视需要而定了。

1.2 为什么要使用单元测试?

“单元测试让生活更轻松。”

        这听起来像是某句广告词,它究竟效果如何呢?

        让我们回到现实,假定我们正在开发一个网站程序。首先我们会将整个程序设计为三层:数据访问层、业务逻辑层和表现层。首先是编写数据访问层,如何测试它是否正确呢?如果没有进行单元测试,那么就得等到业务逻辑层和表现层开发完毕后才能打开页面进行测试。而这中间,业务逻辑层要调用数据访问层,表现层要调用业务逻辑层的代码。如果通过页面发现某个功能没有通过,我们就得进行调试,调试时要一步一步地跟踪代码,好不容易找到bug所在了,原来是数据访问的一个方法里出了问题,把该方法改好了,编译不通过!看来还得修改另外两层的代码,好,把代码都改好了,再次打开页面进行测试,糟糕还是没通过。上面的过程再来一次。。。

        上面这种方式的缺点可以总结为:

        错误难以定位:每次要打开页面、输入值、调试,单元测试可能也需要这些过程,但其工作量则会小很多。 
        执行时间长:较之单元测试,上面的方式显然耗时得多。 
        代码覆盖:可以理解的是,涉及的代码层次越多,就越是难以确定被测代码和测试值之间的关系。我们要覆盖到所有的数据访问层的代码,就要花费很大的精力。 
        在应用了(好的)单元测试后,一切都将变得不同了。我们可以快速定位错误,执行的时间也要短得多,代码覆盖也更容易进行。

        如果一开始就对数据访问层和业务逻辑层进行了良好的单元测试,那么接下来表现层的开发就顺利得多了,我们相信前面的代码已经相当不错了,可以开心地编写后面的代码。一旦出了问题,我们也很容易定位和修改。

        所以说,单元测试让开发人员对自己的代码充满信心,这项看似简单的技术可以让代码变得更加完美,从而让我们的生活更轻松。

1.3 它能给我带来什么?

        引入单元测试是简单的,它本身是充满乐趣的,但我们不会将它交给客户和最终用户。我们得考虑一下,单元测试的目的是什么。首先的一点是,使用单元测试是为了让我们的工作更为轻松。

        当然了,可执行的文档具有自我验证正确与否的优点,在首次编写后就不需很大的工作量了。不像普通意义上的文档,它不会出现与代码不一致的情况(除非你不再执行测试或者让它们一直失败下去)。

        代码的行为与我的期望一致吗?

        最根本的是,我们需要回答一个问题:“这段代码达到我的目的了吗?”先别太关心需求,当前要做的事情是确保一段代码的行为与期望一致。

        代码的行为一直和我的期望一致吗?

        工程师在设计桥梁的时候,必须考虑负载、强风、地震、洪水等等,不能在因洪水出现问题后说,“如果风和日丽就不会有问题了”。

        程序开发亦是如此。记得以前开发的时候,一个模块开发完毕后,我就把那些相关的页面打开,按正常的顺序输入正确的数据把功能走一遍,如果都通过了就交给测试人员。而测试人员最喜欢的一件事就是找出开发人员的bug,他们会想法按错误的顺序输入错误的数据,然后等着bug出现。

        结果是,我的程序出了一堆bug,它们大多数都是因为错误的顺序或者错误的数据,我气愤地跟他们说,“你如果按正常的顺序输入正确的数据就不会有问题了!”

        但事实是,测试桥梁时,不能仅选择在风和日丽的一天,仅让一辆车顺利通过,这远远不够。同样的,在测试代码的行为是否与期望一致时,需要确认:在任何情况下,这段代码是否都与期望一致:比如文件不存在、权限不足、索引越界、网络断掉的时候。

        我可以依赖单元测试吗?

        不能依赖的代码是没有多大用处的。更为糟糕的是,那些我们认为值得信赖的代码(但其实是有bug的)有时候会让我们花费更多的时间去跟踪和调试。

        没有人能够写出完美无缺的代码,但是这没问题——只要我们知道问题的所在就可以了。

        我们希望能够依赖于所编写的代码,并且清楚地知道这些代码的功能和约束。

        单元测试能否表达我的意图?

        编写单元测试,一个额外的好处就是它能够帮助我们表达代码的意图。在效果上,它就像是可执行的文档,说明了在用各种条件调用代码时,我们对代码行为的期望。

        当团队的其他成员看到测试代码后可以将其作为代码用法的示例。如果他发现了一个遗漏的测试用例,他会很快知道:代码可能不支持这个用例。

1.4 如何进行单元测试?

        单元测试是较为简单易学的技术,如果遵循一些指导性原则,学习会变得更为容易和有效。

        首先要考虑的是在编写测试方法之前,如何测试那些可疑的方法。有了大概一个想法之后,可以在编写实现代码的时候,或者在此之前,编写测试代码。

        下一步,要运行测试本身,或者所在模块的其他测试,甚至是整个系统的测试,前提是它们要运行得相当快。重要的是所有测试用例都要通过,而不是仅仅新加的那个。这种基本的回归测试(Regression Test)可帮助我们避免对其他的测试带来间接的破坏。

        我们还要借助于单元测试框架来进行测试,这样可以大大提高效率。相关的知识可在后面的文章中介绍。

1.5 可是我还是不想测试

        看了上面的这些介绍后,也许你能理解单元测试的必要性,也许你还在犹豫,而且还有不少的理由。

21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号