`
flash7783
  • 浏览: 309474 次
  • 性别: Icon_minigender_1
  • 来自: 南昌
社区版块
存档分类
最新评论

单元测试实践小结

阅读更多

在系统开发过程种使用单元测试,会带来很多的的好处,最明显为:
When you become convinced of the value of comprehensive unit testing, you’ll find that it begins to influence how you write code, and the frameworks you choose to use。

应用单元测试,首先要解决的是单元测试的关注点
测试的关注点在于测试逻辑,只要有逻辑就要写测试代码。测试的手段就是验证所有被测试方法的所有产出物,包括:
1. 测试方法的返回值
2. 测试方法的执行流程
例如:

public class DomainService {
private static TheDAO dao = new TheDAO ();
public ReturnObject findByCond(String) {
        return (ReturnObject)dao.getBeanByCondition("select * from ReturnObject where cond="+ paramter, ReturnObject.class);
    }
}


在对于测试findByCond方法,有两个测试用例:
A.测传递给TheDAO.getBeanByCondition的参数的正确性,如果参数不是”select * from ReturnObject where cond=?”和ReturnObject.class则返回为null。
B.测返回的对象正确性。
 
特别是第二点,在商业应用上比较常见的。通常有些方法无明显output,通常是执行写表操作的。对于这样的方法就是测试它的执行流程。当然这些方法本身包含逻辑的。
一个简单的解决方法是利用Access Log来实现(虽然这样的测试不多,而写的case代码也看着怪怪的)。

public class ServiceExample{
    private DatabaseDao1 dao1;
    private DatabaseDao2 dao2;
 
    public void noOutputMethod(){
if(...)
            dao1.update(...);
    if(...)
            dao2.delete();
}
}


 
相关的测试代码可以这样:

public class MockDatabaseDao1 implements DatabaseDao1 {
private Map map;
public void setMap(Map map){
     this.map = map;
 }
 
public void update(args){
     map.put("MockDatabaseDao1.update", args);
}
}


   

public class MockDatabaseDao2 implements DatabaseDao2 {
    private Map map;
 
    public void setMap(Map map){
        this.map = map;
    }
 
    public void delete(args){
        map.put("MockDatabaseDao2.delete", args);
}
}


 

public class ServiceExampleTestCase{
    private Map map = new HashMap();
    public void testNoOutputMethod(){
        DaoTest test = new DaoTest();
   DatabaseDao1 dao1 = new MockDatabaseDao1();
   dao1.setMap(map);
   dao2.setMap(map);
   DatabaseDao2 dao2 = new MockDatabaseDao2();
    test.setDao1(dao1);
    test.setDao2(dao2);
    test.noOutputMethod();
    assertEquals(new Boolean(true), new Boolean(map.containsKey("MockDatabaseDao1.update")));
    assertEquals(new Boolean(true), new Boolean(map.containsKey("MockDatabaseDao2.delete"))); 
    }
}
 

例子只测试执行流程,实际实践中还可以验证所有的参数。 
我们还可以考虑利用AOP来改进这个测试方法。then, we needn't to do the same work,each time. We repeat it only once.

讨论完测试的关注点后,需要看看实际面临的具体困难
职责不明确 
   类或类方法的职责不明确,违反SRP原则.一个类或方法处理了本不该有它处理的逻辑,使得单元测试需要关心过多的外部关联类
静态方法
    静态方法使得调用者直接面对实际的服务类,难以通过其他方式替换其实现,也难以扩展
直接访问对象实例
调用者直接实例化服务对象,从而使用服务对象提供的服务.同静态方法一样,直接面对其服务类
J2se和J2ee标准库或者其他类库
    标准类库中有非常多的接口调用使得调用者难以测试 e.g JNDI, JavaMail, JAXP
准备数据及其困难
    编写测试用例需要外部准备大量的数据

针对这些困难,可用解决方法如下: 
重构系统
    对于职责不明确的代码,只有通过重构才可以达到单元测试的目的。
Self-Delegate test pattern
   针对于class的测试,使用自代理测试模式, 使得测试时,可以重写被测试类的一些方法.达到测试的目的.通过extend class override methods来实现。Inner class mock方法也一样。不过这种方法比较别扭
编写Stubs和Mock object 
   1.   接口的mock比较容易,测试时,编写stubs和mock object来辅助测试,是非常重要的技术. Mock object分动态mock和静态mock.采用EasyMock可以很好的实现动态mock。 
   2.  具体类的mock,也很简单,通常利用子类继承的方式实现,利用cglib框架可以很好大达到测试目的。 
   3.  静态方法的mock。静态方法由于是直接面对服务对象,比较麻烦。不过,并非不可以测试,实际我们可以利用classpath的特点来实现。
方法很简单,mock类与建立一个将被mock的类的package,class name以及方法签名完全一样,但方法实现却是mock过的。在运行测试用例时,把mock类打成jar(不一定要这么做), 在配置classpath时确保,该jar的位置在当前class之前,就可以实现替换。代码如下:StaticMock.rar
使用成熟单元测试框架
   除了最基本的Junit外,Opensource提供了很多非常有价值的单元测试框架,熟练使用这些工具,可以提高测试的效率。包括对准备大量的数据,以及j2ee的框架代码。
   现有代码的可选自动化测试工具:
   1. POJO:JUnit, JMock或者EasyMock
   2. Data Object:DDTUnit。准备大量数据。
   3. Dao:DBUnit。初始化数据库。批量产生数据库数据。
   4. EJB: MockEJB或者MockRunner
   5. Servlet:Cactus
   6. Struts:StrutsUnitTest
   7. XML:XMLUnit
   8. J2EE: MockRunner
   9. GUI: JFCUnit, Marathor
   10. Other: JTestCase(采用XML定义测试过程)

分层架构下的单元测试
1 Web层的单元测试
主要测试Controller的数据结构化逻辑
如果View是利用模板引擎的,需要测试页面的控制脚本是否正确。

2 Domain Service的单元测试
包括业务规则和业务流程。
Service有四种参与对象,如下:
   1. Domain Object
   2. Dao对象
   3. 其它Service服务。
   4. 工具类
产出物:
   1. 返回值包括POJO,和结构化的数据(如XML)
   2. 传递给流程节点的参数值。
特点:
   概念上,业务逻辑和业务流程是相对独立的。实际代码,虽然一些业务逻辑是相对独立的。但是有一些业务逻辑与流程合在一起。由于业务逻辑有明确的返回值,业务规则可以独立成一个方法,其是有显示的返回值,这样UnitTest就可以focus在业务规则的测试上。而业务流程通常没有显示的返回值,在很多实践中表现为写表动作,测试比较麻烦。
   同时,不过的实际情况是业务规则和业务流程是合并在一起的。

测试的应覆盖:
1. 返回值包括POJO,或者结构化的数据如XML可以利用XMLUnit来解决。
2. 流程节点的访问,以及传递给流程节点的参数值。即对业务流程的测试,可以使用上面的访问点的方法。

3.Dao的单元测试
第一个面临的问题是:做Dao数据访问层的单元测试时机。another word也就是要不要做单元测试。
几种情况是不用测试的
1. 如果Dao就是简单的CRUD,那么不用测;在未来当我们使用1.5的范型后,这些CRUD只要在父类做一边里就可以了。
2. 如果hbm文件是自动生成的,那也不用测。
以下是要测的情况:
1. 如果hbm文件是手工写的,那么需要你保证hbm的正确性。如何测试,后面再说。
2. 如果Dao中包括了一些组合查询,那么这是一种逻辑,就应该去测;如果Dao的查询还包含了某个排序机制,这个排序逻辑依据的是业务字段,那么也是要测的。(理由是:这些逻辑可以在java代码实现,不过是性能太差了,但是既然java代码的逻辑要测,那么我们没有理由不去测在sql中的逻辑)。

第二个问题如何测试:
0. 测试数据准备
可以将BA准备的数据导出。在利用Excel编辑产生一批数据。
但是每个UnitTest测试本身应该focus一个关注点上,所以每个UnitTest的数据保持在较少的水平上。
另外由于DBUnit导入数据的顺序是依据sheet的顺序的,请注意把所有外键表在前,否则插入数据时,会报外键不存在错误。

1. 数据库的选择
a.可以直接用小组用的开发数据库。优点:现成的, 所有schema都建好了。缺点:目前数据库的数据干净性无法保证,连接速度太慢。
b.使用hsqldb。优点:利用其内存模式,可以随测试程序启动,简单小巧,schema可以自行定义,每人各自一套互不影响。 缺点:无法提供PLSQL支持。出于UnitTest本身的要求,以及性能上考量,大部分情况下,建议使用hsqldb,对于涉及到PLSQL的,需要mock处理。

2.测试hbm
利用hsqldb内存数据库,在setup的时候,利用hibernate的SchemaExport工具类,将hbm导出成数据库的schema,如果有确实有潜在问题,那么测试程序将不通过。

3.测试Dao
很简单了,调用dao程序操作。对于save,update和delete操作的。需要利用原始的connection执行查询验证。对于组合查询的和逻辑排序的,就是一般的做法了。

4.在使用DBUnit时,测试非只读操作时,我们经常会采用 DatabaseOperation.CLEAN_INSERT 策略.在关联表比较多时,效率会很差.因为每次setUp,tearDown时都会重新先Delete,再Insert所有的数据.另外,我们还有一种数据库操作测试的策略,就是使用真实数据库,在每次操作完毕后都回滚事务.

分享到:
评论

相关推荐

    单元测试实践小结(很详细)

    单元测试是一种软件开发中的关键实践,它涉及到对代码的各个最小可测试单元进行独立验证,以确保它们按预期工作。这种测试方法可以帮助开发者在早期发现并修复错误,提高代码质量,减少集成和系统测试阶段的问题。 ...

    单元测试实践小结[5]

    单元测试实践小结[5] 软件测试 7.XML:XMLUnit 8.J2EE:MockRunner 9.GUI:JFCUnit,Marathor 10.Other:JTestCase(采用XML定义测试过程) 分层架构下的单元测试 1Web层的单元测试 主要测试Controller的数据结构化...

    单元测试小结.

    本小结将详细探讨单元测试及其在Java开发中常用的工具——JUnit。 JUnit是Java编程语言中最流行的一种单元测试框架,它为开发者提供了创建和执行单元测试的简便方法。JUnit基于注解,使得测试用例的编写简洁且易于...

    单元测试实践小结[1]

    单元测试实践小结[1]单元测试工具应用单元测试,首先要解决的是单元测试的关注点。测试的关注点在于测试逻辑,只要有逻辑就要写测试代码。测试的手段就是验证所有被测试方法的所有产出物,包括:1.测试方法的返回值2...

    软件单元测试实践小结[2]

    软件单元测试实践小结[2]单元测试代码相关的测试代码可以这样:publicclassMockDatabaseDao1implementsDatabaseDao1{privateMapmap;publicvoidsetMap(Mapmap){this.map=map;}publicvoidupdate(args){map.put(...

    单元测试实践小结[6]

    单元测试实践小结[6]软件测试产出物:1.返回值包括POJO,和结构化的数据(如XML)2.传递给流程节点的参数值。特点:概念上,业务逻辑和业务流程是相对独立的。实际代码,虽然一些业务逻辑是相对独立的。但是有一些业务...

    单元测试实践小结[3]

    单元测试实践小结[3]软件测试例子只测试执行流程,实际实践中还可以验证所有的参数。我们还可以考虑利用AOP来改进这个测试方法。then,weneedn'ttodothesamework,eachtime.Werepeatitonlyonce.讨论完测试的关注点后...

    单元测试实践小结[4]

    单元测试实践小结[4]软件测试编写Stubs和Mockobject1.接口的mock比较容易,测试时,编写stubs和mockobject来辅助测试,是非常重要的技术.Mockobject分动态mock和静态mock.采用EasyMock可以很好的实现动态mock。2....

    单元测试实践小结[7]

    单元测试实践小结[7]软件测试第二个问题如何测试:0.测试数据准备可以将BA准备的数据导出。在利用Excel编辑产生一批数据。但是每个UnitTest测试本身应该focus一个关注点上,所以每个UnitTest的数据保持在较少的水平...

    学习ruby单元测试小结

    在编程领域,单元测试是一种非常重要的实践,它能确保代码的正确性和稳定性。在Ruby语言中,Test::Unit是用于编写单元测试的标准库。本文将深入探讨Ruby单元测试,特别是Test::Unit框架,并结合学习过程中的体验进行...

    2单元测试基础.pptx

    通过遵循良好的单元测试实践,如使用单元测试框架、编写可读性强的测试代码以及定期重构,开发者可以构建出更加稳定、可维护的软件系统。在快速变化的软件行业中,掌握单元测试的基础知识和技巧,对于任何级别的...

    7-单元测试-每章小结1

    在软件开发过程中,单元测试和代码扫描工具是两个至关重要的环节,...在实践中,软件质量的保证是一个综合的过程,涉及到设计、编码、测试和持续改进等多个环节,而单元测试和代码扫描工具正是这一过程中的关键工具。

    软件测试知识小结

    ### 软件测试知识小结 #### 测试理论知识 **什么是软件测试?** 软件测试是为了寻找软件中的错误而运行软件的过程。一个成功的测试意味着发现了迄今为止尚未发现的错误。软件测试的根本目的是发现软件缺陷、提升...

    部编人教版四年级语文下册第七单元测试卷与单元知识小结.pdf

    【四年级语文下册第七单元测试卷与单元知识小结】 在小学四年级语文学习的过程中,第七单元是一个关键的学习阶段,涵盖了丰富的语文知识点。这个单元的测试卷与知识小结旨在帮助学生巩固所学,提升理解和应用能力。...

    新精通版四年级下册小学英语全册单元知识点小结.docx

    【新精通版四年级下册小学英语全册单元知识点小结】 小学四年级下册的英语学习涵盖了多个单元,每个单元都有其特定的核心词汇、了解词汇、核心句型和了解句型。这些知识点旨在帮助学生建立基本的英语词汇量,理解和...

    程序员实习小结.doc

    【程序员实习小结】 这篇文档记录了一位程序员的实习经历,从中我们可以提炼出以下几个重要的知识点: 1. **软件工程流程**:实习生初次接触大规模软件开发,经历了从需求分析、概要设计、详细设计到编码、测试等...

    Java测试新技术TestNG和高级概念.part1

    1.5 本章小结 第2章 测试设计模式 2.1 针对失败而测试 2.2 工厂 2.3 数据驱动测试 2.4 异步测试 2.5 测试多线程代码 2.6 性能测试 2.7 模拟和桩 2.8 依赖的测试 2.9 继承和annotation范围 2.10 测试分组 2.11 ...

    程序员实习小结.pdf

    这篇《程序员实习小结》是实习生对于编程实习经历的总结,从中我们可以提炼出多个与IT行业相关的知识点: 1. **软件开发流程**:文中提到的需求分析、概要设计、详细设计、编码、单体测试、系统测试等是软件开发的...

    如何使用CppUnit进行单元测试

    #### 五、小结 通过上述步骤,您可以开始使用CppUnit来为您的C++项目编写高质量的单元测试了。掌握CppUnit不仅能帮助您提高代码质量,还能让您更好地遵循测试驱动开发的最佳实践。希望本指南能让您在C++单元测试的...

Global site tag (gtag.js) - Google Analytics