文章列表
 
您正在查看 "tdd(测试驱动)" 分类下的文章

2009-06-10 13:13
由于博客搬家到了http://www.daniel-journey.com/ 该文章页被搬到新的地址http://www.daniel-journey.com/archives/73   
为什么要编写单元测试?原因是单元测试有不少的优点,能够给我们的工作带来很大的帮助。
单元测试的优点

    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. 推广和运用单元测试需要比较大的投入。只有在每个开发人员都编写了足够的、质量好的单元测试代码,大家才能真正享受到单元测试带给我们的好处。在达到这种层度以前,还需要不少实现和资源的投入。

总结

   虽然单元测试也有一些缺点和负面的效应,但跟单元测试的优点比较起来,为了克服和解决这些缺点所在的付出是值得的。

相关文章
单元测试的基本概念
推荐一个优秀的Java单元测试框架——JTester
关于《单元测试的七种境界》的自我总结
 
2009-06-10 9:42
   这几天在准备一个给部门内部的单元测试的介绍,所以想说明一些单元测试的基本概念。google了一下,发现一篇整理了不错的文章《单元测试的基本概念》,所以直接转载了,原文内容如下。
  
    要减少软件中的错误数目,方法之一就是拥有一个专业的测试组,其工作就是尽一切可能使软件崩溃。不幸的是,如果拥有测试组,那么即使是经验丰富的开发人员,也会倾向于花费较少的时间来保证代码的可靠性。

    软件界有一句俗语:“开发人员不应该测试他们自己的代码”。这是因为开发人员对自己的代码了如指掌,他们很清楚如何采用适当的方法对代码进行测试。尽管这 句俗语很有道理,但却忽略了非常重要的一点 - 如果开发人员不对自己的代码进行测试,又如何知道代码能否按照预期的方式运行?

    简单说来,他们根本无从得知。开发人员编写那种运行不正常或只在某些情况下运行正常的代码是一个严重的问题。他们通常只测试代码能否在很少的情况下正常运行,而不是验证代码能够在所有情况下均正常运行。

    发现软件错误的情况有很多:

    1.由首次编写代码的开发人员发现。
    2.由尝试运行代码的开发人员发现。
    3.由组中的其他开发人员或测试人员发现。
    4.作为产品大规模测试的一部分。
    5.由最终用户发现。

    如果在第一种情况下发现软件错误,则修复错误比较容易,成本也很低。情况越靠后,修复软件错误的成本就越高;修复一个由最终用户发现的软件错误可能要耗费 100 或 1000 倍的成本。更不用说用户通常因为软件错误导致工作无法继续,而一直等到下一个版本才能解决问题。
    如果开发人员能够在编写代码期间发现所有的软件错误,那就再好不过了。为此,您必须编写能在编写代码时运行的测试。

    测试是软件开发的重要环节之一。按照软件开发的过程测试可分为:单元测试、功能测试、性能测试、性能测试、集成测试、系统测试、域测试(Field test)等。我们这里将主要研究的是面向程序员的单元测试。

    什么是单元测试

    单元测试是开发者编写的一小段代码,用于检验被测代码中的一个很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特 定函数的行为。例如,你可能把一个很大的值放入一个有序list 中去,然后确认该值出现在list 的尾部。或者,你可能会从字符串中删除匹配某种模式的字符,然后确认字符串确实不再包含这些字符了。

    单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。

    为什么要使用单元测试

    我们编写代码时,一定会反复调试保证它能够编译通过。如果是编译没有通过的代码,没有任何人会愿意交付给自己的客户。但代码通过编译,只是说明了它的语法正确;我们却无法保证它的语义也一定正确,没有任何人可以轻易承诺这段代码的行为一定是正确的。

    幸运,单元测试会为我们的承诺做保证。编写单元测试就是用来验证这段代码的行为是否与我们期望的一致。有了单元测试,我们可以自信的交付自己的代码,而没有任何的后顾之忧。

    单元测试有下面的这些优点:

    1、它是一种验证行为。

    程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支缓。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。

    2、它是一种设计行为。

    编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。

    3、它是一种编写文档的行为。

    单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。

    4、它具有回归性。

    自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。

    单元测试的范畴

    如果要给单元测试定义一个明确的范畴,指出哪些功能是属于单元测试,这似乎很难。但下面讨论的四个问题,基本上可以说明单元测试的范畴,单元测试所要做的工作。

    1、 它的行为和我期望的一致吗?

    这是单元测试最根本的目的,我们就是用单元测试的代码来证明它所做的就是我们所期望的。

    2、 它的行为一直和我期望的一致吗?

    编写单元测试,如果只测试代码的一条正确路径,让它正确走一遍,并不算是真正的完成。软件开发是一个项复杂的工程,在测试某段代码的行为是否和你的期望一 致时,你需要确认:在任何情况下,这段代码是否都和你的期望一致;譬如参数很可疑、硬盘没有剩余空间、缓冲区溢出、网络掉线的时候。

    3、 我可以依赖单元测试吗?

    不能依赖的代码是没有多大用处的。既然单元测试是用来保证代码的正确性,那么单元测试也一定要值得依赖。

    4、 单元测试说明我的意图了吗?

    单元测试能够帮我们充分了解代码的用法,从效果上而言,单元测试就像是能执行的文档,说明了在你用各种条件调用代码时,你所能期望这段代码完成的功能。

    不写测试的借口

    到这里,我们已经知道了使用单元测试的种种理由。但目前的实际情况是大多数程序员不进行单元测试,或只进行简单的单元测试。下面是一些他们常用的接口:

    1、 编写单元测试太花时间了。

    我们知道,在开发时越早发现BUG,就能节省更多的时间,降低更多的风险。如果你仍然认为在编写产品代码的时候,还是没有时间编写测试代码,那么请先考虑下面这些问题:

    1)、对于所编写的代码,你在调试上面花了多少时间。
    2)、对于以前你自认为正确的代码,而实际上这些代码却存在重大的bug,你花了多少时间在重新确认这些代码上面。
    3)、对于一个别人报告的bug,你花了多少时间才找出导致这个bug 的源码位置。
    回答完这些问题,你一定不再以“太花时间”作为拒绝单元测试的借口。

    2、 运行测试的时间太长了。

    合适的测试是不会让这种情况发生的。实际上,大多数测试的执行都是非常快的,因此你在几秒之内就可以运行成千上万个测试。但是有时某些测试会花费很长的时间。这时,需要把这些耗时的测试和其他测试分开。通常可以每天运行这种测试一次,或者几天一次。

    3、 测试代码并不是我的工作。

    你的工作就是保证代码能够正确的完成你的行为,恰恰相反,测试代码正是你不可缺少的工作。

    4、 我并不清楚代码的行为,所以也就无从测试。

    如果你实在不清楚代码的行为,那么估计现在并不是编码的时候。如果你并不知道代码的行为,那么你又如何知道你编写的代码是正确的呢?

    5、 但是这些代码都能够编译通过。

    我们前面已经说过,代码通过编译只是验证它的语法通过。但并不能保证它的行为就一定正确。

    6、 公司请我来是为了写代码,而不是写测试。

    公司付给你薪水是为了让你编写产品代码,而单元测试大体上是一个工具,是一个和编辑器、开发环境、编译器等处于同一位置的工具。

    7、 如果我让测试员或者QA(Quality Assurance)人员没有工作,那么我会觉得很内疚。

    你并不需要担心这些。请记住,我们在此只是谈论单元测试,而它只是一种针对源码的、低层次的,为程序员而设计的测试。在整个项目中,还有其他的很多测试需要这些人来完成,如:功能测试、验收测试、性能测试、环境测试、有效性测试、正确性测试、正规分析等等。

    8、 我的公司并不会让我在真实系统中运行单元测试。

    我们所讨论的只是针对开发者的单元测试。也就是说,如果你可以在其他的环境下(例如在正式的产品系统中)运行这些测试的话,那么它们就不再是单元测试,而是其他类型的测试了。实际上,你可以在你的本机运行单元测试,使用你自己的数据库。

    总而言之,单元测试会让我们的开发工作变得更加轻松,让我们对自己的代码更加自信。无论是大型项目还是小型项目,无论是时间紧迫的项目还是时间宽裕的项目,只要代码不是一次写完永不改动,编写单元测试就一定超值,它已成为我们编码不可缺少的一部分。

其他相关资料

Unit testing

 
2009-06-01 18:29
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 或者我联系

相关资料
项目主页http://code.google.com/p/java-tester/
 
2009-05-28 21:03
文章内容迁移到了 http://www.daniel-journey.com/archives/182
moreUnit是一个Eclipse的插件,为编写Java的单元测试提供了很多辅助的功能,提高了编写和管理Java单元测试(Junit或者TestNG)的效率。主要的功能有以下几点
1.标志出哪些类是有单元测试支持的。
2.标志出那些方法是有单元测试支持的。
3.对类或者方法的名称进行重命名的时候会相应的重命名单元测试中的名称。
4.移动类的时候moreUnit会相应的移动单元测试类。
5.在工作类和测试类之间通过快捷方式实现(Ctrl+J)快速跳转,这个功能真是很方便

moreUnit的安装、配置和使用都很简单,moreUnit的文档也都配有截图,这里就不在做特别的说明了。提供链接,大家直接到项目网站上去看吧,moreUnit文档链接


相关资源
moreUnit
 
2009-05-24 16:48
   一直以为TestNG才有参数化测试解决方案,不想在看了Parameterising JUnit tests 的文章以才知道JUnit4中也提供了参数化测试的功能,于是简单的学习了一下。
   什么是参数化测试?
   很多时候一个方法的实现需要支持多种Case或者说从测试的角度来看需要测试多种边际条件。例如一个字符串大小写转换的函数需要实现把employee_info这样以“_”分隔的字符串转换成相应的大写字母开头的字符串EmployeeInfo。针对这样的实现方法进行单元测试就需要考虑支持测试多种情况:
    1.测试空字符串
    2.测试一般处理情况
    3.测试尾字母大写的情况.......
    4.处理Null
    5......
    在不使用参数化测试方法的情况下,我们需要针对各种情况编写assert:
    1. assertEquals(“”,“”);
    2.assertEquals("EmployeeInfo","employee_info");
3. assertEquals("EmployeeInfoA","employee_info_a");
    4.assertEquals(null,null);
    5......
   这种解决方式是不是感到有很多重复,而参数化测试就是解决这种情况的绝好方法。
   下面来介绍一下参数化测试的开发方式
   参数化测试实例
   1. 为准备使用参数化测试的测试类指定特殊的运行器 org.junit.runners.Parameterized。
   2. 为测试类声明几个变量,分别用于存放期望值和测试所用数据。
   3. 为测试类声明一个使用注解 org.junit.runners.Parameterized.Parameters 修饰的,返回值为 java.util.Collection 的公共静态方法,并在此方法中初始化所有需要测试的参数对。
   4. 为测试类声明一个带有参数的公共构造函数,并在其中为第二个环节中声明的几个变量赋值。
   5. 编写测试方法,使用定义的变量作为参数进行测试。
/*********************************************************************************************/
@RunWith(Parameterized.class)
public class StringTransformerTest {
    private String testData;
    private String expectedData;

    public StringTransformerTest(String testData, String expectedData) {
        this.testData = testData;
        this.expectedData = expectedData;
    }

    @Test
    public void testTransfer() {
        assertEquals(expectedData, StringTransformer.transfer(testData));
    }

    @Parameters
    public static Collection getParameters() throws Exception {
        return Arrays.asList(new Object[][] {
                { "employee_info", "employeeInfo" }, // 测试一般的处理情况
                { null, null }, // 测试 null 时的处理情况
                { "", "" }, // 测试空字符串 时的处理情况
                { "employee_info", "EmployeeInfo" }, // 测试当首字母大写时的情况
                { "employee_info_a", "employeeInfoA" }, // 测试当尾字母为大写时的情况
                { "employee_a_info", "employeeAInfo" } // 测试多个相连字母大写时的情况
                });

    }
}
/*********************************************************************************************/

   运行效果如下图


参考资料
Parameterising JUnit tests
单元测试利器 JUnit 4
 
2009-05-16 11:29

   文章转载至http://www.daniel-journey.com/archives/152

这几年了陆陆续续的学习过不少技术,但真正能够对自己的工作起很好帮助的是在不多,测试驱动是其中之一。前段时间国外的有位程序员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倍或者更多。

相关资料

为什么要编写单元测试

单元测试的基本概念
推荐一个优秀的Java单元测试框架——JTester

单元测试的七种境界

The 7 Phases of Unit Testing

[译文]:单元测试的七种境界

 
2009-04-21 6:34
由于博客搬家到了http://www.daniel-journey.com/ 该文章页被搬到新的地址http://www.daniel-journey.com/archives/185

   在单元测试用例的开发过程中遇到需要测试私有方法的地方往往会给开发人员带来很大的困扰。
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

 
 
   
 
 
文章分类
 
   
 
文章存档
 
     
 
最新文章评论
  

如果我想学习了解单元测试的话,我想知道我学到什么水平、或者说了哪些内容后才可以
 

按照这种操作,创建分支,点击ok后,提示access to 'http://xxxx/svn' forbidden,这
 

今天刚了解了这个设计原则,摊开来讲的话,博大精深
 

能详细阐述一下就好了
 

thx
   
帮助中心 | 空间客服 | 投诉中心 | 空间协议
©2012 Baidu