测试驱动的设计和开发 ( Test Driven Design and Development ) 基础篇

Slides:



Advertisements
Similar presentations
第 9 章 测试部署  9.1 测试  9.2 部署. 9.1 测试  应用测试:使用 JUnit 单元测试框架 测试的目的是检验开发结果是否满足规定需求,测试是保证软件质量的一个重要手段, 在软件开发过程中是不可缺少的组成部分。 单元测试与集成测试分别有各自的定义:在实际开发中,两者之间的界定是模糊的。
Advertisements

面向侧面的程序设计 方林博士 本文下载地址:
第四章 类、对象和接口.
3.2 Java的类 Java 类库的概念 语言规则——程序的书写规范 Java语言 类库——已有的有特定功能的Java程序模块
JAVA 编 程 技 术 主编 贾振华 2010年1月.
第16章 代理模式 Website:
Java程序设计教程 第一讲 Java概述.
四資二甲 第三週作業 物件導向程式設計.
Java Programming Hygiene - for DIDC
2017/3/17 第七课 带上X光眼镜检查软件-续.
MVC Servlet与MVC设计模式.
第二章 JAVA语言基础.
類別與物件 Class & Object.
第八章 分析與設計階段 – 物件導向設計(OOD)
Android App 系統開發教學 Luna 陳雯琳 2014/12/18
C# 程式設計 第一部分 第1-4章 C# 程式設計 - 南華大學資管系.
Ch02 視窗Swing套件 物件導向系統實務.
Ch08 巢狀類別 物件導向程式設計(II).
JUnit 軟體測試架構 JUnit Software Testing Framework
Android + JUnit 單元測試 建國科技大學資管系 饒瑞佶 2012/8/19V4.
程式設計實作.
2.1 基本資料型別 2.2 變數 2.3 運算式與運算子 2.4 輸出與輸入資料 2.5 資料型別轉換 2.6 實例
第5章 面向对象程序设计 本章要点 5.1 面向对象程序设计概述 5.2 Java语言的面向对象程序设计 5.3 方法的使用和对象数组
API设计实例分析 通用IO API.
例外處理(Exception Handling)
JAVA vs. SQL Server 建國科技大學 資管系 饒瑞佶 2013/4 V1.
Java基础 JavaSE异常.
程式敘述執行順序的轉移 控制與重複、方法 Lecturer:曾學文.
中国散裂中子源小角谱仪 的实验数据格式与处理算法 报告人:张晟恺 中国科学院高能物理研究所 SCE 年8月18日
本單元介紹何謂變數,及說明變數的宣告方式。
西南科技大学网络教育系列课程 高级语程序设计(Java) 第五章 继承、接口与范型.
程式設計實作.
软件建模训练(6-2) 设计模式 张静 南京理工大学计算机科学与工程学院.
Java语言程序设计 第五部分 Java异常处理.
黄海波 & 陶万山 with contribution by 劳晖
Java程序设计 第9章 继承和多态.
王豐緒 銘傳大學資訊工程學系 問題:JAVA 物件檔輸出入.
Visual Studio Team System 简介
中国矿大计算机学院杨东平 第5章 接口和包 中国矿大计算机学院杨东平
第九章 測試工具與測試管理系統.
第十一讲 测试驱动开发.
单元测试工具XUnit 任课老师:黄武 下午2时20分 25.
Agile Software Development
軟體工程:如何開發軟體? 把它看成是一件工程。 那麼就會有一些工具、技術、方法,也有管理的議題。
Java程序设计 第2章 基本数据类型及操作.
C/C++/Java 哪些值不是头等程序对象
第11章 系统结构与包模型模型.
程序设计工具实习 Software Program Tool
JAVA 编 程 技 术 主编 贾振华 2010年1月.
《JAVA程序设计》 语音答疑 辅导老师:高旻.
Aspect Oriented Programming
第二章 Java基本语法 讲师:复凡.
徐迎晓 复旦大学软件学院 实现模型 徐迎晓 复旦大学软件学院.
Lightweight Data-flow Analysis for Execution-driven Constraint Solving
主编:钟元生 赵圣鲁.
Web安全基础教程
Interfaces and Packages
第二章 Java语法基础.
Delphi 7.0开发示例.
第二章 Java基本语法 讲师:复凡.
JAVA 程式設計與資料結構 第三章 物件的設計.
第2章 Java语言基础.
Usage Eclipse 敏捷方法工具介紹 實驗室網站:
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
第二章 Java基础语法 北京传智播客教育
DEV310 Microsoft Visual Studio 2005托管代码调试
輸出執行結果到螢幕上 如果要將執行結果的文字和數值都「輸出」到電腦螢幕時,程式要怎麼寫? class 類別名稱 {
第6章 继承和多态 伍孝金
Summary
Presentation transcript:

测试驱动的设计和开发 ( Test Driven Design and Development ) 基础篇 Charles Huang & Watson Tao With contribution by Eric Lao www.chinaxp.org 1

你的代码工作吗? “这段代码很简单,不可能出错” “我试过了,它是正常工作的呀” “我用Debugger测试过了,我遍历了所有程序分支,内存中的值都是对的” 最好的方法是写一段另外的代码来证明它,让电脑来告诉 我们它是工作的。 2

Acceptance Test( Functional Test ) Regression Test Nightly Test XP中的测试 Unit Test Acceptance Test( Functional Test ) Regression Test Nightly Test Stress Test 所有的测试都应该独立地自动的运行 3

什么是单元测试(Unit Test) 单元测试是一段能够放在批处理中自动运行的,用来测试Classes的 程序。单元测试测试一小段代码或一个足够小的功能。单元测 试程序调用这小段代码或功能,并验证返回的结果是否符合预先设 定的结果。 每个单元测试至少应该有两个测试例子( Test Case ): Negative Positive 单元测试是软件工程的一个关键部分。 4

什么是Acceptance Test Acceptance Test are programs or scripts configured to test that packages (groups of clusters of classes) meet external requirements and achieve goals, such as performance. They include screen-driving programs that test GUIs from without. Acceptance Test是对软件做End-To-End的测试,衡量软件是否符合 用户需求的指标,也就是验收测试。 5

什么是Regression Test “Regression testing is the process of validating modified parts of the software and ensuring that no new errors are introduced into previously tested code.” 一句话,Regresstion Test就是要重新测试所有的代码和功能。 Regression Test和Development Test的不同在于Regression Test 需要重用已经建立的所有的测试单元(Unit Test )和功能测试套件(Functional Test)。 Regression Test的基础是完整的自动单元测试和功能测试。 6

什么是Nightly Test Nightly Test就是每晚自动运行所有的Unit Test和Acceptance Test。 Nightly Test是XP中的Continuous Test的一个练习(Practice)。 Nightly Test可以准确的反映项目开发的进度和质量。 7

Nightly Test Nightly Test是软件开发中一个保证开发之质量的最有效的方法,也 是衡量软件之质量和开发效率的最好的指标。 Nightly Test就是每天工作结束,所有的代码都Check in到Source Control后,自动运行所有的Unit Test和Function Test。测试的结果 应该自动分发给开发人员和管理层。 两个指标数值: 测试例子的通过率 – 单元测试必须是100%通过。Functional Test 应该按计划的通过。 单元测试的覆盖率 – 表明有多少Class被测试过和测试的完善程度。 8

测试优先的编程 在写任何代码之前,先写它的Unit Test。 “Never write a line of functional code without a broken test case” Kent Beck Test-First Programming是一种测试技术吗? Test-First Programming首先是一种分析方法。它迫使程序员仔细思考要做什么和不要做什么(而不是如何具体的实现)。特别是各种例外的情况,并用程序语言正式的写下来。这就好像在程序员的任务和程序员之间签订了一个清晰的正式合同。 Test-First Programming是一种设计方法。Unit Test测试的事程序,而不是一个想法。程序员必须清晰的定义程序的界面才能写出它的Unit Test。而这时程序员是不知道(也不需要知道)里面的具体逻辑是如何实现的。程序员只需要考虑Class的界面和功能(Responsibility)。啊,你在做OO设计了。 Test-First Programming是一种质量控制方法( Quality Control )。如何控制质量呢?如何知道我的程序是否运行呢?我会不会漏了什么?运行一下Unit Test。 Test-First Programming是一种重构和优化的方法。我们总希望自己的代码可以漂亮,运行的效率高,所以我们会不断地去改进。可是如何保证改进和优化后的质量呢?会不会越改越糟?答案还是Unit Test。 Test-First Programming不是通常意义上的测试技术,它的目的也不是仅仅用来测试你的代码。 Test-First Programming是一种面向对象的开发方法。 9

什么是Test-Driven Design (TDD) 在写产品代码之前,先写它的单元测试( Unit Tests ) 没有单元测试的Class不允许作为产品代码 单元测试例子决定了如何写产品代码 不断地成功运行所有的单元测试例子 不断的完善单元测试例子 Test-Driven Design是把需求分析,设计,质量控制量化 的过程! 10

为什么会出现TDD 现实中的设计(Design)和测试(Testing): 面对一个新的开发任务,往往第一个念头就是如何去实现它呢? “好像是这样做的” 感觉上差不多了。 抓起任务就开始编码,一边写,一边修改和设计。 哎,时间很紧。我先把任务实现了,然后再好好测试。 还是不工作,时间不多了。做个快速但丑陋的修改吧。等有空来再来重新整理这些代码吧。 用Debugger运行几次代码,走完所有的我认为可能的分支。我感觉这些代码应该行了。提交吧。 哎,我也知道该写一些自动的单元测试来把刚才在Debugger中的测试走一遍。可是那是很多的活啊。 这种情况要作自动测试太复杂了。还是手工作一下测试好了。 11

为什么会出现TDD(Continue) 程序员心中的测试: 很郁闷的工作。对啊,程序员该做些新的,有创意的东西嘛。写一些新的功能会更有趣些。 我知道这些代码会工作的。我的经验和感觉都这样告诉我。只要没人乱改我的代码,应该就没问题。再说这些边缘情况几乎不可能出现了。 测试是QA的工作。 自动测试太花时间(我要赶Deadline),不值得。 12

Test-Driven Design and Development 如何面对这些现实和想法 Test-Driven Design and Development 真的能行? 试一试! 13

如何做Test Driven Design and Development 再开发一个新的功能之前 首先确定你要做什么(不是要如何做!!) 比如说一个论坛的增加用户的功能,我们需要又一个method来增加一个用户: public void addAccount( Account account ) 当然包括成功增加一个用户(在数据库中插入一条纪录) 还包括如果已经由一个相同的用户,应该返回一个用户已存在的消息 OK,我们知道这个method中的这段代码要做什么,而且这段代码也足够简单。 14

如何做Test Driven Design and Development (Continue ) 然后为这个功能(Method)写单元测试例子( Unit Test ) 单元测试例子要覆盖这个Method的 “做什么”。 所以我们至少有了两个测试例子: Test Case 1: 测试成功增加一个用户 Test Case 2: 测试增加一个已存在的用户 其他边缘情况测试: Test Case 3: 传入的Account对象为NULL 15

如何做Test Driven Design and Development (Continue ) 写Production代码 我们清楚知道这段代码需要做什么。因为我们有另一段代码摆在那里,清晰的表明这段代码的Contracts。 不用多,也不能少,只需要能实现再Unit Test中的Contracts和能够通过它的Unit Test。 16

如何做Test Driven Design and Development (Continue ) 运行Unit Test 如果顺利通过,你已经很好的完成了你的任务。 如果没通过,修补代码直到能通过Unit Test为止。 如果出现在Unit Test中没预先设定的结果,在Unit Test中增加一个Test Case,修补代码直到通过所有的Test Case为止。 17

TDD和PSP Personal Software Process的Development Analysis Design Code Build Test Test-Driven Design and Development Build Run Test Analysis Design Code Unit Test Code 18

XP采用了TDD TDD是Extreme Programming中必须遵行的一个方法。TDD是XP中Pair XP正是因为采用了TDD才能够做到每天的代码都是Production Code和每个小的Release都能提供具备Production质量的代码并投入使用。 有了TDD,XP才能降低风险,去拥抱变化。 有了TDD,XP才能在计划的时间内完成计划质量的代码。 有了TDD,XP才能减少Code<->Fix环节,从而减少项目成本。 有了TDD,XP Team才能对自己的工作充满自信。 19

TDD防止Over-Engineering 不愿为一个Method写测试例子或者认为现在没有必要测试改Method, 那这个Method多半是现在不需要的。 20

TDD,程序员和管理层 对程序员来说,通过运行Unit Test和Functional Test,每天下班的时 候都可以清楚的知道自己的代码是work的。 对管理层来说,通过Nightly Test的结果,每天一早都清楚的知道项 目的质量和开发进度。 21

XP中谁来写Tests Developer: Unit Test Acceptance Test( Functional Test ) Customer: Acceptance Test Customer为每一个User Story写Functional Test。但通常用户并不 具备设计和开发Functional Test的能力,需要程序员的帮助。 22

什么时候写Tests? 如果你要写一个新的功能,请先写她的测试例子 如果你要在没有经过测试的代码上写新的功能,请先写目前代码的测试例子 如果你要Fix一个Bug,请先为这个Bug写一个测试例子 如果你要Refactor没有测试过的代码,请先写一个测试例子 如果你发现一个边缘例外值,请为她写一个测试例子 23

Extreme Unit Junit Java Class的测试Framework JFCUnit Java Swing app的测试Framework Catus Java Server Side( EJB, Servlet )的测试Framework HTMLUnit Html Page的测试Framework HTTPUnit Html Page的测试Framework CPPUnit C++测试Framework .NetUnit .Net app的测试Framework …… 24

Junit( A sample) Junit是由kent Beck和Erich Gamma 编写的一个open source的测试 框架,用来编写可重复的测试例子。 测试论坛中的增加用户method public class AccountDAOmySql implements AccountDAO { /** * Add a user account * * @param Account - A account object that contains the user info,like userName, * password,email */ public void addAccount( final Account account ) throws SQLException, AccountAlreadyExistException{ …… } 25

Junit( A sample) 1.为对应的Java Class建立一个TestCase。Unit Test Case应该放在 和Business Class相同的Package中,但在不同的的物理位置。 package org.redsoft.forum.dao.mysql import junit.framework.TestCase; import junit.framework.TestSuite; import junit.framework.Test; public class AccountDAOmySqlTest extends TestCase { public AccountDAOmySqlTest(String name) { super(name); } 26

Junit( A sample) 2. Override setUp() and tearDown() Database Connection, File I/O或Mock Objects等) 相应的,可以在tearDown()中释放资源(Database Connection,File I/O和Mock Objects等) public class AccountDAOmySqlTest extends TestCase { … … private MysqlFixture mysqlFixtureIns = new MysqlFixture(); public void setUp() throws Exception { mysqlFixtureIns.setUp(); } public void tearDown() throws Exception { mysqlFixtureIns.tearDown(); 27

Junit( A sample) 3.为被测试的Method写Test Case …… public void testAddAccountNormal(){ AccountDAOmySql dao = new AccountDAOmySql(); Account account = new Account(USER_NAME,"charles","charles_hhb@hotmail.com"); try{ dao.addAccount( account ); Account account_new = dao.findByUserName( account.getUserName() ); assertEquals("Expecting charles",account.getUserName(),account_new.getUserName() ); assertEquals("Expecting charles",account.getPassword(),account_new.getPassword() ); assertEquals("Expecting charles@hotmail.com",account.getEmail(),account_new.getEmail() ); dao.removeAccount( account.getUserName() ); }catch( final Exception e ){ e.printStackTrace(); fail("Unexpected exception::" + e.toString()); } 28

Junit( A sample) public void testAddAccountAlreadyExist(){ AccountDAOmySql dao = null; Account account = null; try{ // Add an Account … … dao.addAccount( account ); fail("AccountAlreadyExistException expected"); }catch( final SQLException e ){ e.printStackTrace(); fail("Unexpected exception::" + e.toString()); }catch( final AccountNotFoundException notFound ){ notFound.printStackTrace(); fail("Unexpected exception: " + notFound.toString() ); }catch( final AccountAlreadyExistException ex ){ // Pass dao.removeAccount( account.getUserName() ); }catch( final SQLException sql ){ sql.printStackTrace(); fail("Unexpected exception "); } 29

Junit( A Sample) 运行这个Unit Test。 Junit提供两种运行界面:Swing(junit.swingui.TestRunner) C:\sandbox\forum>java -classpath ./classes;./lib/junit.jar;./lib/mysql_jdbc.jar;./lib/Tidy.jar;./lib/struts.jar junit.swingui.TestRunner org.redsoft.forum.dao.mysql.AccountDAOmySqlTest 30

Junit( A Sample) Text界面( junit.textui.TestRunner ) 31

Junit Test Suite Test Suite用来运行所有的Unit Tests Test Suite的数型结构: org.redsoft.forum.AllTest | |----org.redsoft.forum.dao.AllTest | | | |-------org.redsoft.forum.dao.mysql.AllTest |----org.redsoft.forum.util.AllTest 每个Package Level都由一个AllTest Test Suite 在每个Test Suite中, 加入在本package level中的所有单元测试例子( Unit Test Cases ) 加入子Package level中的所有AllTest Suite 32

Junit Test Suite 33 package org.redsoft.forum.dao; public class AllTests { public static void main(String args[]) { junit.textui.TestRunner.run(suite()); } public static Test suite() { TestSuite suite = new TestSuite(); // 加入子package中的AllTest suite suite.addTest(org.redsoft.forum.dao.mysql.AllTests.suite()); // 加入本package level中的Unit Test case suite.addTestSuite(MysqlDataSourceTest.class); return suite; }//EOC 33

JFC Unit 一个Junit的Extension,用来测试Swing-based的Application。 一个最简单的Sample:测试一个Login Screen 34

JFC Unit 代码片断:测试图形界面 代码片断:设置测试环境 35 JDialog dialog; JButton exitButton = ( JButton ) helper.findNamedComponent( "ExitButton", loginScreen, 0 ); assertNotNull( "Could not find the Exit button", exitButton ); JButton enterButton = ( JButton ) helper.findNamedComponent( "EnterButton", assertNotNull( "Could not find the Enter button", enterButton ); JTextField userNameField = ( JTextField ) helper.findNamedComponent( "LoginNameTextField", assertNotNull( "Could not find the userNameField", userNameField ); assertEquals( "Username field is empty", "", userNameField.getText( ) ); JTextField passwordField = ( JTextField ) helper.findNamedComponent( "PasswordTextField", assertNotNull( "Could not find the passwordField", passwordField ); assertEquals( "Password field is empty", "", passwordField.getText( ) ); 代码片断:设置测试环境 private LoginScreen loginScreen = null; private TestHelper helper = null; public LoginScreenTest( String name ) { super( name ); } protected void setUp( ) throws Exception { super.setUp( ); helper = new JFCTestHelper( ); loginScreen = new LoginScreen( "LoginScreenTest: " + getName( ) ); loginScreen.setVisible( true ); } protected void tearDown( ) throws Exception { loginScreen = null; helper.cleanUp( this ); super.tearDown( ); 35

利用Ant来做Nightly Test 使用Ant中的两个Tasks来完成自动运行Nightly Test 产生一个XML个是的结果报告 <junit printsummary="yes" haltonfailure="yes"> <test name="org.redsoft.forum.AllTests” haltonfailure="no" outfile="result" > <formatter type="xml" /> </test> </junit> 产生一个XML个是的结果报告 再利用JunitReport来产生一个可供浏览的结果文件。 <junitreport todir="./reports"> <fileset dir="./reports"> <include name="TEST-*.xml"/> </fileset> <report format="frames" todir="./report/html"/> </junitreport> 36

测试例子覆盖率 利用NOUnit来获得测试例子的覆盖率 (http://nounit.sourceforge.net/) 37

Unit Tests:100% always 任何时候如果Unit Tests的出现错误(Junit的进度指示显示红色), XP Team的首要工作就是修补Unit Tests直至Junit的进度指示为绿色。 如果不修复出错的测试例子,就会出现滚雪球效应,在未知质量代 码基础上的开发只会导致更多的未知质量的代码。软件质量的基石 就开始崩溃。 38

Software Quality Management 如何衡量?CMM Level4 -- 软件产品的质量管理是被预先计划的  -- 测量方法和目标是被预先定义和计划的  -- 质量管理的进度是被预先计划的  39

Reference Kent Beck,Extreme Programming Explained:Embrace Change C2.com Junit.org www.chinaxp.org 40