存档

‘测试驱动’ 分类的存档

面向对象设计、设计模式、代码维护、重构、单元测试关系的思考笔记

2009年9月12日 admin 没有评论

这篇文章是我个人对面向对象设计、设计模式、代码维护、重构、单元测试之间关系的一些思考笔记,内容写的比较粗糙,在以后会不断地完善文章的内容、和示例。

例如,我们有一个场景需要实现在某个人给Blog发了一个Comment以后,就发一份邮件给Blog的host,在邮件中说明comments的内容,外加一个批准这个Comment的url link。

一个典型的实现可能如下面这段代码

public class CommentAction {
    private CommentService commentService;

    private MailSender mailSender;

  /**

   发布评论方法。

  */

    public void doComment(Rundata rundata) {

        Comment comment = getComment(rundata);
        Boolean resesult = commentService.doComment(comment);
        if (resesult) {
            Mail commentMail = genContent4CommentNotificationMail(comment);
            mailSender.sendMail(commentMail);
        }
    }

  /**

   获取评论数据

*/

    private Comment getComment(Rundata rundata) {
        Comment comment=new Comment();
        comment.setCotent((String)rundata.get("content"));
        comment.setPoster((String)rundata.get("poster"));
        comment.setTimeSubmited((Date)rundata.get("timeSubmited"));
        return comment;
    }

/**

   产生发送邮件的内容。

*/

    private Mail genContent4CommentNotificationMail(Comment comment) {
        Mail mail = new Mail();
        mail.setBody(comment.getContent());
        mail.setTitle("您的Blog有一封新的评论");
        return mail;
    }

}

我们在描述CommentAction 的功能描述应该是“向系统发表一篇Comment然后,生成提醒邮件发送给Blog的Host”。

从业务发生变化的角度来分析,以下几种业务的变更都会导致CommentAction 的频繁修改

  • 评论内容和格式的变化
  • 增加新的提醒方式
  • Email提醒的内容、格式发生变化
  • 等等

从面向对象的设计原则来分析这块代码,从“向系统发表一篇Comment然后,生成提醒邮件发送给Blog的Host”这个描述中开发人员应该体会到CommentAction 包含了至少2项功能,意味着这个类目前的实现已经破坏了单一责任原则。CommentAction 就是一个God类,包含了实现这个UC几乎全部的逻辑。

从代码维护的角度来分析这块代码,随着业务逻辑越来越复杂,相应的实现也会变得越来越复杂,代码越来越不清晰,代码的维护越来越困难。

从单元测试的角度来分析这块代码,对CommentAction 的doComment方法的测试,需要Mock commentService和MailSender 这两个类。单元测试的开发、维护都会困难。CommentAction 其中某个逻辑的修改都有可能破坏CommentAction 其他的单元测试。

所以解决的方法是对CommentAction 进行重构。我做的重构如下,仅供参考。

public class CommentAction {
    private CommentService commentService;
    private List<CommentEventListener> commentEventListeners;

    public void doComment(Rundata rundata) {
        Comment comment = getComment(rundata);
        commentService.doComment(comment);
        fireEvents(comment);
    }

    private void fireEvents(Comment comment) {
        CommentEvent commentEvent=new CommentEvent(comment);
        for (CommentEventListener commentEventListener : commentEventListeners) {
            commentEventListener.fireEvent(commentEvent);
        }
    }

    private Comment getComment(Rundata rundata) {
        Comment comment=new Comment();
        comment.setCotent((String)rundata.get("content"));
        comment.setPoster((String)rundata.get("poster"));
        comment.setTimeSubmited((Date)rundata.get("timeSubmited"));
        return comment;
    }   
}

public class CommentEvent   {

    private Comment comment;
    public CommentEvent(Comment comment) {
        this.comment=comment;
    }
    public Comment getComment() {
        return comment;
    }

}

public interface CommentEventListener {

    void fireEvent(CommentEvent commentEvent);
}

public class CommentEventMailNotifactionListener implements CommentEventListener {
    private MailSender mailSender;

    private Mail genContent4CommentNotificationMail(Comment comment) {
        Mail mail = new Mail();
        mail.setBody(comment.getContent());
        mail.setTitle("您的Blog有一封新的评论");
        return mail;
    }
    @Override
    public void fireEvent(CommentEvent commentEvent) {
        mailSender.sendMail(genContent4CommentNotificationMail(commentEvent.getComment()));
    }
}

重构以后的CommentAction中主要进行的就是comment的发布,另外就是触发了commentEvent事件。邮件的发送逻辑被移植到了CommentEventMailNotifactionListener 来实现,解除了CommentAction和MailSender 类的耦合。CommentAction 不再是个God类,实现这个UC的逻辑由几个类协作完成。

从业务发生变化的角度来分析重构以后的代码

  • 评论内容和格式的变化——只需要修改CommentAction
  • 增加新的提醒方式——只需要扩展新的CommentEventListener
  • Email提醒的内容、格式发生变化——只需要修改CommentEventMailNotifactionListener

从代码维护的角度来分析重构以后的代码,CommentEventMailNotifactionListener 、CommentAction 都只关注他们各自的逻辑,代码清晰、实现简单、维护也很容易,修改了之后对其他模块的破坏几率也很小。

从单元测试的角度来分析这块代码,对CommentAction 的doComment方法的测试,只需要Mock commentService这个类就可以了。对邮件的提醒的单元测试只需要对CommentEventMailNotifactionListener 进行单元测试就可以了。重构前后单元测试的开发、维护的难度和成本都会得到控制。

从上面这个例子中,我们可以总结出以下几点:

  • 编写易读、易维护、可扩展性好的代码是我们的最大目标。
  • 要实现这个目标需要遵循很多面向对象的设计原则。
  • 设计模式的运用可以帮助我们达到这个目标,后者说使用合理的设计模式就可以实现这个目标。
  • 实现这个目标需要通过代码的不断重构来达到。
  • 保证重构以后的代码还是可以继续工作的,就需要单元测试来保护我们的每一次重构。

翻译:编写可测试的代码

2009年8月18日 admin 没有评论

在编写单元测试代码的过程中,我们时常会发现某些类或者方法不可测或者不易测,也就是通常所说的代码可测性比较差。当出现这种情况的时候往往也意味着我们的代码存在着问题。在Guide to Writing Testable Code这篇文章中作者列举了多种影响代码可测性的情况,当出现以下问题的时候我们需要及时地对我们的代码加以调整。以下就是对Guide to Writing Testable Code的翻译。

构造函数做了实际的工作

  • 在构造函数或字段声明的时候使用了new 关键字
  • 在构造函数或字段声明的时候使用了静态方法
  • 在构造函数中做了字段赋值以外的事情
  • 在构造函数完成以后,对象还不能完全初始化(警惕initialize方法)
  • 在构造函数中存在流程控制的语句(条件语句、循环语句)
  • 在构造函数中存在复杂对象关系构造
  • 使用了初始代码块

跟合作对象耦合太深

  • 传过来的对象不直接使用,而只是用来获取其他的对象
  • 对象方法调用的层次不止一层。
  • 代码中存在context, environment, principal, container,或 manager之类可以的对象名称

容易被破坏的全局状态和单例模式

  • 使用了单例模式
  • 使用了静态字段或静态方法
  • 使用了静态代码块
  • 使用了服务定位器模式

类承担了太多的职责

  • 总结这个类的功能时需要用上“和”、“以及”这样的词汇。
  • 新成员阅读和理解这个类存在困难
  • 类中的某些字段只在几个特定的方法中被用到了
  • 这个类存在只对方法参数操作的静态方法。

参考资料

原文:Guide to Writing Testable Code

分类: 测试驱动 标签:

单元测试实践总结报告

2009年8月17日 admin 没有评论

最近在部门内部推广单元测试,阶段性的总结总是免不了的。下面是这次单元测试总结的主要内容

1.单元测试所增加的工作负担

在开发阶段引入单元测试以后会增加一些工作量,有哪些工作量呢?

  • 直接的单元测试开发的工作量。这部分的工作量是未使用的单元测试的开发过程所没有的。
  • 由单元测试引发的设计变动、代码重构带来的工作量 。虽然,不管使用单元测试技术都会可以在开发、bugfix阶段 进行设计变动、代码重构等工作,但引入了单元测试以后,这样的行为会进行的更为频繁,当然带来的结果就是更好的设计、更易维护的代码,这个是我们所期望的,也是我们引入单元测试的目的之一。同样这部分工作也会带来一定的工作量。
  • 对Bug进行单元测试补充的工作量。在不使用单元测试的情况下,遇到bug我们会直接调整生产代码,而运用TDD的项目中,我们鼓励的单元测试方法是
        1. 修改任何代码前先编写一个会失败的简短测试——即将出现的bug用一个单元测试用例来表达,当然由于bug的存在单元测试会失败。
        2. 然后再修改生产代码,使之前测试失败的单元测试通过,这就意味着这个bug被成功修复。
          这种工作方式很有效,但的确会给Bug的修复又多一点带来工作量。
  • 单元测试维护工作量。单元测试代码跟我们的生产代码一样需要好的设计和维护。当已有单元测试所支持的生产代码由于业务需求或者设计发生变动的时候,相应的单元测试代码也需要加以维护。

2.单元测试所减少的工作负担

在《1.单元测试工作量分析》中我们说的是单元测试带来的工作负担,但单元测试同样会给我们的开发过程带来效率的提高

  • 提高开发效率。利用单元测试进行开发、bug修复的话,整个过程可以不依赖应用程序的容器,使得代码跟容易被运行。
  • 减少重复工作,利用单元测试进行开发、bug修复的话,开发人员"反复录入测试数据、查看运行结果"的工作模式会单元测试自动运行的模式所替代。
  • 减少Debug的时间。

单元测试还有很多优点,但还不会很直接的减少我们的工作负担

综合"1.单元测试所增加的工作负担"和"2.单元测试所减少的工作负担"提到的内容,虽然"1.单元测试所增加的工作负担"所增加的工作负担会被"2.单元测试所减少的工作负担"抵消掉一部分,但整体而言单元测试时会增加开发工作量的,会以牺牲time-to-market为代价

3.如果看待单元测试增加的工作负担

由于我们增加了单元测试的开发工作,直接影响的是time-to-market的时间,所以大家一定会问以下这些内容:

  • 单元测试开发实际增加的工作量是多少?
  • 单元测试开发带来的好处整么体现?

第一个问题其实比较容易回答,只要大家通过一个实际的项目就能测算出来,我的感觉是单元测试所增加的工作负担会是原来的1/3,也就是说原本一个模块3天完成的,现在会变成4天。不过这只是我个人的计算。

第二个问题会比较难以回答。主要的原因有下面几个

  • 单元测试所带来的价值很多时候是体现在软件的编码、设计、质量上门的,这些内容多数是软件的内在质量,跟软件的外在质量(完成了哪些功能、输入输出的验证做的好不好)不同难以被直接的发现和体验到。
  • 如果单元测试的成果要体现在测试阶段的话,也需要涉及到QA本身的工作过程和模式,不能够直接体现在Bug Fix时间的减少,但一定可以减少bug数量等等。
分类: 测试驱动 标签:

在单元测试中如何测试私有方法

2009年7月31日 admin 没有评论

在单元测试用例的开发过程中遇到需要测试私有方法的地方往往会给开发人员带来很大的困扰。
http://blog.developers.ba/post/2009/04/12/Unit-testing-private-methods-yes-or-no.aspx 这篇blog中,博主也提到了类似的问题。
1. 需要对私有方法进行测试吗?
2. 如何测试?是调整私有方法的访问权限或是通过public来测试。
3. 对私有方法进行测试是不是意味着代码中有了bad smell。
4….
接下来谈谈我的感受吧
1. 是不是需要对一个方法(无论是public或是private)取决于这个函数的复杂度和价值,并不是所有的function都要测试的,除非需要追求很高的测试覆盖率。这样的话是否需要对一个private方法进行测试就看这个private方法是不是比较重要、复杂、容易出错,其实这个原则也适用于Public的方法,你总不会去测试java的setter和getter吧。
2. 很多时候private的方法的测试时可以通过对public方法的测试间接来达到的。一个设计良好的、充分运用TDD的工程完全可以通过对public方法提供完整的用例就可以覆盖private方法。
3. 仅仅为了能够进行单元测试,而采用将private方法改成public方法通常是不合适的,这样会暴露出不该暴露的实现细节,另外接口过多也破坏了SRP(单一职责原则)。
4. 当你非常渴望测试一个private方法的时候,可以仔细评估这个private方法和目前所在类的关系,这样的private方法是不是应该迁移到另一个类中,在另一个类中作为public提供接口给调用方(我通常这么干)。
5. 最后一个测试private的途径就是利用语言提供的反射功能,在testcase中将private方法修改成public的,以此来实现对private方法的测试。

参考资料

Testing private methods, TDD and Test-Driven Refactoring

Unit testing private methods yes or no?

Using Reflection in Unit Tests

分类: 测试驱动 标签:

Eclipse的Java单元测试小工具moreunit

2009年7月30日 admin 没有评论

moreUnit是一个Eclipse的插件,为编写Java的单元测试提供了很多辅助的功能,提高了编写和管理Java单元测试(Junit或者TestNG)的效率。主要的功能有以下几点
1.标志出哪些类是有单元测试支持的。
2.标志出那些方法是有单元测试支持的。
3.对类或者方法的名称进行重命名的时候会相应的重命名单元测试中的名称。
4.移动类的时候moreUnit会相应的移动单元测试类。
5.在工作类和测试类之间通过快捷方式实现(Ctrl+J)快速跳转,这个功能真是很方便
moreUnit的安装、配置和使用都很简单,moreUnit的文档也都配有截图,这里就不在做特别的说明了。提供链接,大家直接到项目网站上去看吧,moreUnit文档链接

分类: ECLIPSE, 测试驱动 标签: ,

在Maven2下用JTester开发单元测试

2009年7月27日 admin 没有评论

JTester简介

JTester是一个非常优秀的单元测试框架,主要的特性有以下内容

1、在unitils的基础,集成了jmock功能。
       2、在hamcrest断言的基础上,实现了fluent interface断言。
       3、改造了jmock expectation参数断言为fluent interface形式
       4、提供了将普通的pojo对象序列化到文件,然后再从文件中反序列化回来的功能,用于在对象复杂的情况下,直接录制接口(远程接口)调用返回的对象,以供下次测试或调试使用。
       5、使用wiki代替xml来准备测试数据。
       6、实现了更加丰富的断言。
       7、提供了hibernate annotation环境下,直接使用内存数据库进行db测试。
       8、提供了hibernate annotation环境下,Open Test in Session的实现。
   熟悉Java单元测试的同学应该能体会到对Java程序如果只是单纯的使用Junit或是TestNG这样的基础单元测试框架往往很难应对各种复杂的单元测试情况,所以势必要借助很多第三方的框架和技术(easymock,jmock,dbunit等等)。而这些框架和技术的学习又会增加学习的成本和难度,所以有人在这些java基础单元测试的工具基础上开发一些测试框架(如unitils)将多种Java单元测试技术整合在一起,提高开发效率。 JTester的目的也是一样,在作者精心的开发和维护的基础上,已经拥有了强大的功能和稳定的质量。希望对Java的单元测试技术感兴趣的同学能加以使用,对JTester有功能需求的同学可以跟作者 Darui.wu 或者我联系。

JTester的Maven2配置

首先,在Pom.xml中加入JTester的Repository URL

<repositories>
        <repository>
            <id>jtester-maven</id>
            <name>JTester</name>
            <url>http://java-tester.googlecode.com/svn/maven2/
            </url>
        </repository>
</repositories>

引入JTester测试依赖包

<dependencies>
        <dependency>
            <groupId>org.jtester</groupId>
            <artifactId>jtester</artifactId>
            <version>${jtester.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-all</artifactId>
            <version>${hamcrest.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>${mockito.version}</version>
        </dependency>
        <dependency>
            <groupId>org.jmock</groupId>
            <artifactId>jmock</artifactId>
            <version>${jmock.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>hamcrest-core</artifactId>
                    <groupId>org.hamcrest</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>hamcrest-library</artifactId>
                    <groupId>org.hamcrest</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jmock</groupId>
            <artifactId>jmock-legacy</artifactId>
            <version>${jmock.version}</version>
        </dependency>
        <dependency>
            <groupId>org.unitils</groupId>
            <artifactId>unitils</artifactId>
            <version>${unitils.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>ant</artifactId>
                    <groupId>ant</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>5.8</version>
            <classifier>jdk15</classifier>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jline</groupId>
            <artifactId>jline</artifactId>
            <version>0.9.94</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.5.6</version>
        </dependency>
    </dependencies>
    <properties>
        <jtester.version>0.7.1</jtester.version>
        <testng.version>5.8</testng.version>
        <unitils.version>2.2</unitils.version>   
        <hamcrest.version>1.1</hamcrest.version>
        <jmock.version>2.5.1</jmock.version>
        <mockito.version>1.7</mockito.version>
        <dbunit.version>2.4.3</dbunit.version>
    </properties>

 

如果一起OK的话,你的Proeject中应该已经成功导入了JTeser0.7.1的jar包。


相关资料


项目主页http://code.google.com/p/java-tester/

分类: 测试驱动 标签:

关于《单元测试的七种境界》的自我总结

2009年7月24日 admin 没有评论

这几年了陆陆续续的学习过不少技术,但真正能够对自己的工作起很好帮助的是在不多,测试驱动是其中之一。前段时间国外的有位程序员Karl Seguin整理了一篇文章叫The 7 Phases of Unit Testing ,很快在译言网上也有了翻译《单元测试的七种境界》,作为一位测试驱动开发的粉丝也像借这篇文章整理一下自己的学习过程和体验

1. 以各种借口拒绝单元测试Unit Test,比较常用的是“你没有足够的时间(进行单元测试)”。

——无论是对单元测试的老手还是新手编写单元测试还是有一定得工作量的,而且单元测试也需要掌握大量的测试框架和工具(光一个junit或testng你很难工作地很happy)。所以在这个阶段开发人员往往会觉得单元测试很难写、很费时,自然而然会使用没有足够的时间(进行单元测试)的借口,其实在这个阶段开发人员需要积极地学习和掌握测试框架和理解单元测试理念。

2. 尝试单元测试并且立刻开始在自己的博客商鼓吹单元测试和测试驱动开发Test Driven Development的好处。

——开发人员在这个阶段学习很掌握了一些单元测试的工具并在实际工作中加以的运用,并很好的解决了一些问题,意识到了单元测试的价值。我自己也向同事和同学介绍过相关的技术,希望大家对相关的技术能很好的学习和运用,现在回想那个时候对单元测试的技术的掌握和理解都是不完整的,只能说是初窥门径而已。

3. 单元测试一切。为了能够完成单元测试,而将私有private的方法和属性修改为内部internal;为了达到单元测试覆盖率100%而测试getter() 和 setter() 属性(方法)。

——这样的阶段很明显,特别是遇到private,static方法的测试时会感到很麻烦,所以往往采用了一些不优美的解决方式,目的是能够对相关的方法和类进行单元测试,但没有从根本上意识到是自己的设计有问题,从而导致可测试比较差(testability)。至于对getter和setter 方法进行测试到是没有过,可能只自己所在的公司一直都没有片面的强调过测试100%覆盖率吧。

4. 无法忍受脆弱的单元测试,在没有弄明白是什么的时候,就匆忙转向“集成测试"。

——单元测试也是代码,只要是代码就会有设计、编码上共同的问题,比如设计模式的运用、重复代码的问题。在无法理解和单元测试中“单元”和“隔离”这两个名词的情况下,会想要通过集成测试来实现单元测试。我自己没有运用过集成测试的工具,但用dbunit直接模拟数据库的情况,从而将多个类“集成”起来测试是这个阶段最常用的单元测试方法。实际上用dbunit直接模拟数据库也是非常脆弱和繁琐的,mocking才是王道。

5. 发现了一种模拟 mocking 框架,并且乐于使用强制语义(strict semantics)。

——mocking是单元测试中不可缺少的重要组成,Java的单元测试方案中Easymock和Jmock是两个成熟的Mock框架。但 Mocking的学习和理解可能是单元测试工具中最具有难度的地方了,通过运用Mocking你会发觉之前很多工作(比如数据库模拟)都是浪费时间、精力和无效的。

6. 模拟mock所有可能模拟mocked的对象。

——通过在单元测试中运用Mocking真正贯彻了单元测试的“单元”和“隔离”的原则,不过Mocking是在件繁琐和困难的事情,这时候就需要考虑什么是必须要mock的、什么可以不mock的。

7. 开始真正有效单元测试。

——恭喜你终于达到了这个阶段,你已经将面向对象设计、设计模式、单元测试、重构等一些技术都融汇到了一起,你终于可以根据自己的意愿编写真正有效的单元测试了。在这个阶段可能你掌握或有了一套测试框架,这套测试框架整合了junit、testng、jmock、easymock、dbunit、xumlunit、unitils等一系列你测试工具使你的编写单元测试效率是之前的3-4倍或者更多。

分类: 测试驱动 标签:

为什么要编写单元测试-单元测试的优势及优点

2009年6月22日 admin 没有评论

    为什么要编写单元测试?原因是单元测试有不少的优点,能够给我们的工作带来很大的帮助。
单元测试的优点
1.帮助开发人员编写代码,提升质量、减少bug。如果大家分析一下我们bug原因的构成,我想有会有一部分bug的原因是开发人员在编写工作代码的时候没有考虑到某些case或者边际条件。造成这种问题的原因很多,其中很重要的一个原因是我们对工作代码所要完成的功能思考不足,而编写单元测试,特别是先写单元测试再写工作代码就可以帮助开发人员思考编写的代码到底要实现哪些功能。例如实现一个简单的用户注册功能的业务类方法,用单元测试再写工作代码的方式来工作的话
      开发人员就会先考虑各种场景相关,例如正常注册、用户名重复、没有满足必要的填写内容……等等,之后就会编写相关的测试用例
      public Class UserSerivceTest(){
          public userRegister_Ok(){
              ……
          }
          public userRegister_nameDuplicated(){
              ……
          }
          public userRegister_emailEmpty(){
              ……
          }
      }
      编写单元测试代码的过程就是促使开发人员思考工作代码实现内容和逻辑的过程,之后实现工作代码的时候,开发人员思路会更清晰,实现代码的质量也会有相应的提升。
2. 提升反馈速度,减少重复工作,提高开发效率。开发人员实现某个功能或者修补了某个bug,如果有相应的单元测试支持的话,开发人员可以马上通过运行单元测试来验证之前完成的代码是否正确,而不需要反复通过发布war包、启动 jboss、通过浏览器输入数据等繁琐的步骤来验证所完成的功能。用单元测试代码来验证代码和通过发布应用以人工的方式来验证代码这两者的效率差很多,看到很多开发人员每天要反复执行N次发布脚本(antx之类的工具)真是痛苦。
3.保证你最后的代码修改不会破坏之前代码的功能。项目越做越大,代码越来越多,特别涉及到一些公用接口之类的代码或是底层的基础库,谁也不敢保证这次修改的代码不会破坏之前的功能,所以与此相关的需求会被搁置或推迟,由于不敢改进代码,代码也变得越来越难以维护,质量也越来越差。而单元测试就是解决这种问题的很好方法(不敢说最好的)。由于代码的历史功能都有相应的单元测试保证,修改了某些代码以后,通过运行相关的单元测试就可以验证出新调整的功能是否有影响到之前的功能。当然要实现到这种程度需要很大的付出,不但要能够达到比较高的测试覆盖率,而且单元测试代码的编写质量也要有保证。
4. 让代码维护更容易。由于给代码写很多单元测试,相当于给代码加上了规格说明书,开发人员通过读单元测试代码也能够帮助开发人员理解现有代码。很有opensource的项目都有相当量的单元测试代码,通过读这些测试代码会有助于理解生产源代码。
5. 有助于改进代码质量和设计。除了那些大拿们编写的代码,我相信很多易于维护、设计良好的代码都是通过不断的重构才得到的。虽然说单元测试本身不能直接改进生产代码的质量,但它为生产代码提供了"安全网",让开发人员可以勇敢地改进代码,从而让代码的clean和beautiful不再是梦想。
单元测试的缺点
1.单元测试的学习成本比较高。编写单元测试涉及的技术很多,如果只是单纯的使用 Junit或是TestNG这样的基础单元测试框架往往很难应对各种复杂的单元测试情况,所以势必要借助很多第三方的框架和技术(easymock,jmock,dbunit等等),这些框架和技术的学习还是会增加学习的成本和难度。
2.编写单元测试会增加程序员工作量。单元测试跟生产代码是一样的,并不会应为是用来测试的就有所不同,开发人员同样要面对测试代码的编写、维护等工作,也同样要面对避免重复代码等一系列问题,能否写出好的测试代码还是取决于开发人员的设计和编码能力。
3. 推广和运用单元测试需要比较大的投入。只有在每个开发人员都编写了足够的、质量好的单元测试代码,大家才能真正享受到单元测试带给我们的好处。在达到这种层度以前,还需要不少实现和资源的投入。
总结
   虽然单元测试也有一些缺点和负面的效应,但跟单元测试的优点比较起来,为了克服和解决这些缺点所在的付出是值得的。

分类: 测试驱动 标签: , ,