单元测试基础
当今软件测试十分盛行时,本人通过项目实践和个人亲身体会浅谈单元测试,本人一直坚持“用代码说话的原则”,同时也希望个人能给出宝贵意见,共同探讨、共同进步,为中国软件事业有更大的发展共同奋斗!
最早我们项目组开发的项目时,写代码都是从底层一直写到表现层到jsp,然后开发人员在web层调试页面,近乎98%都会报一大堆exception,然后再在代码中加断点一步一步查到底哪一层代码出现问题……,比较好点做法就是在各个类中加上main方法测试,但总体很不理想,给web层开发人员的调试和质量控制人员带来繁重的工作压力;使用单元测试后,针对每一个方法都做严格的把关,大大减少调试的时间;同时质量控制人员返回过来的bug少了近60%,现在对于开发人员写测试用例非常熟练,并且本人根据实际情况对测试用例做了点小小改动(这部分主要在后面代码中详述),带来很好的效果!
单元测试到底给实际开发带来什么好处那?
(1) 首先对于开发人员来说大大减少调试工作的时间,同时也规范了对于代码安全管理(我们知道那些方法是可以调用的);
(2) 对于整个项目来说,有了完整的测试,保证项目最后交付测试有了可靠依据;
(3) 对于测试人员大大减少bug的反馈;
(4) 对于项目经理整个项目达到很好的可控;
(5) 最主要的完整的单元测试给后期维护人员带来很大的便捷!
单元测试好处可能还有很多,但本人只能理解和感悟这么多,希望观者补充!
单元测试配置:
我将使用eclipse+myEclopse给大家介绍关于JUNIT的环境的简单配置;右键点击项目选择“属性”,在弹出窗口中到环境变量中添加junit.jar包,这样下一步我们就可以进行单元测试了;
使用eclipse快速开发test Case:
如下图:右键选择你要测试的类,在新建中点击“JUnit 测试用例”,
弹出对话框,配置测试名称和根目录,添加注释等,再点击“下一步”到下图:
选择你要测试类中的方法,点击完成!便生成测试类的基本框架,如下代码,我们以对一个DAO类测试为例:
(1)import junit.framework.TestCase 和 junit.textui.TestRunner;
(2)继承junit.framework.TestCase ;
(3)自行添加一个main方法 中调用TestRunner.run(测试类名.class);
在方法页面中点击右键在“调试方式”或“运行方式”中点击“JUnit 测试”,就运行测试类!
(1)先执行构造方法public OrgTypeDAOTest(String arg0) ;
(2)再执行初始化数据方法protected void setUp() ;
(4)最后执行protected void tearDown()方法清理对象;
如果测试失败或者错误,将会显示一个红色的亮条;如果测试通过将显示绿色亮条;如下图
这样就把一个整个单元测试操作例子演示完成!
可能对于一个测试类中有多个方法要测试,对于后面看着的确有些困难,因此,我对上测试类进行简单的调整,如下代码:
import junit.framework.TestCase; import junit.textui.TestRunner; import com.zhjy.mock.SpringMock; /** *//** * 举例测试类 */ public class OrgTypeDAOTest extends TestCase ...{ //(1)继承TestCase //private OrgTypeDAO orgTypeDAO; //private OrgTypeVO orgTypeVO; //private String id ; /** *//** * 构造方法 * @param arg0 */ public OrgTypeDAOTest(String arg0) ...{ super(arg0); } /**//* 初时化方法 * @see junit.framework.TestCase#setUp() */ protected void setUp() throws Exception ...{ super.setUp(); //测试初始话数据调用类 orgTypeDAO和 封装数据的对象orgTypeVO } /**//* 执行完清理方法 * @see junit.framework.TestCase#tearDown() */ protected void tearDown() throws Exception ...{ //清空 对象 ;==null //orgTypeDAO =null; //orgTypeVO =null; super.tearDown(); } /** *//** * 主函数 * @param args */ public static void main(String[] args)...{ TestRunner.run(OrgTypeDAOTest.class); } /** *//** * 测试方法 * Test method testOrgTypeInfo */ public void testOrgTypeInfo() ...{ //添加 String id = insertOrgTypeInfo(); //列表 orgTypeList(); //修改 updateOrgTypeInfo(id); //查询 selectOrgTypeInfoById(id); //校验 iExistOrgByOrgTypeId(id); //测试是否重复数据方法(add) isRepeatOrgTypeInfo(orgTypeVO.getName(),""); //获取数据方法(根据名称) selectOrgTypeIdByName(orgTypeVO.getName()); //删除 deleteOrgTypeInfo(id); } /**//* *添加初始数据 */ private void setOrgTypeVOAddInfo() ...{ orgTypeVO.setName("add中海测试"); orgTypeVO.setDescription("add中海测试"); orgTypeVO.setStatus("1"); } /**//* *添加初始数据 */ private void setOrgTypeVOUpdateInfo() ...{ //orgTypeVO.setId(id); orgTypeVO.setName("add中海测试"); orgTypeVO.setDescription("update中海测试"); orgTypeVO.setStatus("1"); } /**//* * 新增方法 * Test method for {@link OrgTypeDAO#insertOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}. */ public String insertOrgTypeInfo() ...{ setOrgTypeVOAddInfo(); String id = null; try...{ id = orgTypeDAO.insertOrgTypeInfo(orgTypeVO); }catch(Exception e)...{ fail("添加通用组织机构失败!"); } return id; } /**//* * 更新方法 * Test method for {@link com.zhjy.mltx.dao.OrgTypeDAO#updateOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}. */ public void updateOrgTypeInfo(String id) ...{ setOrgTypeVOUpdateInfo(); orgTypeVO.setId(id); try...{ orgTypeDAO.updateOrgTypeInfo(orgTypeVO); }catch(Exception e)...{ assertTrue("修改通用组织机构失败!", false); } //查询 orgTypeVO = orgTypeDAO.selectOrgTypeInfoById(id); assertEquals("修改通用组织机构失败!", orgTypeVO.getDescription(), "update中海测试"); } /**//* * 获取数据方法(主健) * Test method for {@link OrgTypeDAO#selectOrgTypeInfoById(java.lang.String)}. */ public void selectOrgTypeInfoById(String id) ...{ orgTypeVO = orgTypeDAO.selectOrgTypeInfoById(id); assertTrue("无法查看一条通用机构名称信息!",orgTypeVO != null); assertEquals("添加通用组织机构失败!", orgTypeVO.getName(), "add中海测试"); assertEquals("修改通用组织机构失败!", orgTypeVO.getDescription(), "update中海测试"); } /**//* * 测试此通用组织机构是否被引用 * Test method for {@link OrgTypeDAO#iExistOrgByOrgTypeId(java.lang.String)}. */ public void iExistOrgByOrgTypeId(String id) ...{ boolean isfalse; try ...{ isfalse = this.orgTypeDAO.iExistOrgByOrgTypeId(id); assertFalse("通用组织机构校验错误!",isfalse); } catch (DataAccessException e) ...{ assertTrue("通用组织机构数据操作错误!!",false); } catch (ObjectNotFoundException e) ...{ assertTrue("id is null!!",false); } } /**//* * 删除 * Test method for {@link OrgTypeDAO#deleteOrgTypeInfo(java.lang.String)}. */ public void deleteOrgTypeInfo(String id) ...{ this.orgTypeDAO.deleteOrgTypeInfo(id); orgTypeVO = this.orgTypeDAO.selectOrgTypeInfoById(id); assertNull("删除通用组织机构失败!", orgTypeVO); } /**//* * 获取数据方法(根据名称) * Test method for {@link OrgTypeDAO#selectOrgTypeIdByName(java.lang.String)}. */ public void selectOrgTypeIdByName(String name) ...{ String orgtypeid = orgTypeDAO.selectOrgTypeIdByName(name); //assertEquals(orgtypeid, id); assertNotNull("未查出来通用组织机构ID!",orgtypeid); } /**//* * 测试是否重复数据方法 * Test method for {@link OrgTypeDAO#isRepeatOrgTypeInfo(com.zhjy.mltx.vo.OrgTypeVO)}. */ public void isRepeatOrgTypeInfo(String name,String id) ...{ //setOrgTypeVOUpdateInfo(); OrgTypeVO orgtypetest = new OrgTypeVO(); orgtypetest.setId(id); orgtypetest.setName(name); boolean isTrue = orgTypeDAO.isRepeatOrgTypeInfo(orgtypetest); //assertEquals("通用组织机构错误数据",isTrue, false); assertTrue("通用组织机构错误数据", isTrue); } /**//* * 列表方法 * Test method for {@link com.zhjy.mltx.dao.OrgTypeDAO#orgTypeList()}. */ public void orgTypeList() ...{ List list = orgTypeDAO.orgTypeList(); assertNotNull("无法获取通用机构名称列表list is null!",list); } } |
处理过程:
(1)把所有以test开头的方法中的方法名称前的test去掉(例如把testOrgTypeList()改为orgTypeList()方法);
(2)同时添加一个以test开头的方法,并调用测试方法;这样做主要是因为执行测试时JUnit(除本身特有的方法出外)只执行以 test为开头的方法;
(3)同时大家注意到我还把测试初始数据放到两个private方法中private void setOrgTypeVOAddInfo()和private void setOrgTypeVOUpdateInfo()方法中供调用;
大家可能认为很简单,就是这样简单的改动带来很大的方便,大家是否注意到,我以后运行测试用例时,只关心public void testOrgTypeInfo() 方法就行了,因为只要各个方法的断言策略定制完成,一般就不会再改动,因此,后期的回归测试还是验收测试,缩小了我们对测试的关注点,同时对后面写 test suite构建整体测试带来极大方便性!其中的好处相信只有使用才能体会!
最新内容请见作者的GitHub页:http://qaseven.github.io/