测试驱动的设计和开发 ( test driven design and development ) 基础篇

40
Copyright 2002 Chinaxp. All rights reserved 1 测测测测测测测测测测 ( Test Driven Design and Development ) 测测测 Charles Huang & Watson Tao With contribution by Eric Lao www.chinaxp.org

Upload: abdul-sawyer

Post on 03-Jan-2016

181 views

Category:

Documents


3 download

DESCRIPTION

测试驱动的设计和开发 ( Test Driven Design and Development ) 基础篇. Charles Huang & Watson Tao With contribution by Eric Lao www.chinaxp.org. “这段代码很简单,不可能出错” “ 我试过了,它是正常工作的呀” “ 我用 Debugger 测试过了,我遍历了所有程序分支,内存中的值都是对的” 最好的方法是写一段另外的代码来证明它,让电脑来告诉 我们它是工作的。. 你的代码工作吗?. Unit Test - PowerPoint PPT Presentation

TRANSCRIPT

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

Copyright 2002 Chinaxp. All rights reserved 1

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

基础篇

Charles Huang & Watson TaoWith contribution by Eric Lao

www.chinaxp.org

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

Copyright 2002 Chinaxp. All rights reserved 2

你的代码工作吗?

• “这段代码很简单,不可能出错”• “我试过了,它是正常工作的呀”• “我用 Debugger 测试过了,我遍历了所有程序分支,

内存中的值都是对的”

最好的方法是写一段另外的代码来证明它 , 让电脑来告诉我们它是工作的。

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

Copyright 2002 Chinaxp. All rights reserved 3

XP 中的测试

• Unit Test

• Acceptance Test( Functional Test )

• Regression Test

• Nightly Test

• Stress Test

所有的测试都应该独立地自动的运行

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

Copyright 2002 Chinaxp. All rights reserved 4

什么是单元测试 (Unit Test)

单元测试是一段能够放在批处理中自动运行的,用来测试 Classes 的程序。单元测试测试一小段代码或一个足够小的功能。单元测试程序调用这小段代码或功能,并验证返回的结果是否符合预先设定的结果。

每个单元测试至少应该有两个测试例子 ( Test Case ):

• Negative• Positive

单元测试是软件工程的一个关键部分。

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

Copyright 2002 Chinaxp. All rights reserved 5

什么是 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 的测试 ,衡量软件是否符合

用户需求的指标,也就是验收测试。

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

Copyright 2002 Chinaxp. All rights reserved 6

什么是 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 的基础是完整的自动单元测试和功能测试。

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

Copyright 2002 Chinaxp. All rights reserved 7

什么是 Nightly Test

Nightly Test 就是每晚自动运行所有的 Unit Test 和 Acceptance Test 。

Nightly Test 是 XP 中的 Continuous Test 的一个练习 (Practice) 。Nightly Test 可以准确的反映项目开发的进度和质量。

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

Copyright 2002 Chinaxp. All rights reserved 8

Nightly Test

Nightly Test 是软件开发中一个保证开发之质量的最有效的方法,也是衡量软件之质量和开发效率的最好的指标。

Nightly Test 就是每天工作结束,所有的代码都 Check in 到 Source

Control 后,自动运行所有的 Unit Test 和 Function Test 。测试的结果

应该自动分发给开发人员和管理层。

两个指标数值:测试例子的通过率 – 单元测试必须是 100% 通过。 Functional Test 应该按计划的通过。

单元测试的覆盖率 – 表明有多少 Class 被测试过和测试的完善程度。

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

Copyright 2002 Chinaxp. All rights reserved 9

测试优先的编程在写任何代码之前,先写它的 Unit Test 。“Never write a line of functional code without a broken test case” Kent BeckTest-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 是一种面向对象的开发方法。

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

Copyright 2002 Chinaxp. All rights reserved 10

什么是 Test-Driven Design (TDD)

Test-Driven Design 是一种开发风格 , 它要求程序员做到:• 在写产品代码之前,先写它的单元测试 ( Unit Tests )• 没有单元测试的 Class 不允许作为产品代码• 单元测试例子决定了如何写产品代码• 不断地成功运行所有的单元测试例子• 不断的完善单元测试例子

Test-Driven Design 是把需求分析,设计,质量控制量化的过程!

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

Copyright 2002 Chinaxp. All rights reserved 11

为什么会出现 TDD

现实中的设计( Design )和测试( Testing ) :• 面对一个新的开发任务,往往第一个念头就是如何去实现它呢?• “好像是这样做的” 感觉上差不多了。• 抓起任务就开始编码,一边写,一边修改和设计。• 哎,时间很紧。我先把任务实现了,然后再好好测试。• 还是不工作,时间不多了。做个快速但丑陋的修改吧。等有空来再来重新整理这些代码吧。

• 用 Debugger 运行几次代码,走完所有的我认为可能的分支。我感觉这些代码应该行了。提交吧。

• 哎,我也知道该写一些自动的单元测试来把刚才在 Debugger 中的测试走一遍。可是那是很多的活啊。

• 这种情况要作自动测试太复杂了。还是手工作一下测试好了。

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

Copyright 2002 Chinaxp. All rights reserved 12

为什么会出现 TDD(Continue)

程序员心中的测试:• 很郁闷的工作。对啊,程序员该做些新的,有创意的东西嘛。写一些

新的功能会更有趣些。• 我知道这些代码会工作的。我的经验和感觉都这样告诉我。只要没人乱改我的代码,应该就没问题。再说这些边缘情况几乎不可能出现了。

• 测试是 QA 的工作。• 自动测试太花时间(我要赶 Deadline),不值得。

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

Copyright 2002 Chinaxp. All rights reserved 13

如何面对这些现实和想法Test-Driven Design and Development

真的能行?

试一试!

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

Copyright 2002 Chinaxp. All rights reserved 14

如何做 Test Driven Design and Development

再开发一个新的功能之前• 首先确定你要做什么(不是要如何做!!)比如说一个论坛的增加用户的功能,我们需要又一个 method 来增加一个用户:public void addAccount( Account account )

当然包括成功增加一个用户(在数据库中插入一条纪录)还包括如果已经由一个相同的用户,应该返回一个用户已存在的消息

OK, 我们知道这个 method 中的这段代码要做什么,而且这段代码也足够简单。

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

Copyright 2002 Chinaxp. All rights reserved 15

如何做 Test Driven Design and Development( Continue )

• 然后为这个功能( Method )写单元测试例子 ( Unit Test )

单元测试例子要覆盖这个 Method的 “做什么”。所以我们至少有了两个测试例子 :Test Case 1: 测试成功增加一个用户Test Case 2: 测试增加一个已存在的用户

其他边缘情况测试:Test Case 3: 传入的 Account 对象为 NULL

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

Copyright 2002 Chinaxp. All rights reserved 16

如何做 Test Driven Design and Development( Continue )

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

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

Copyright 2002 Chinaxp. All rights reserved 17

如何做 Test Driven Design and Development( Continue )

• 运行 Unit Test

如果顺利通过,你已经很好的完成了你的任务。如果没通过,修补代码直到能通过 Unit Test为止。如果出现在 Unit Test 中没预先设定的结果,在 Unit Test 中增加一个 Test Case ,修补代码直到通过所有的 Test Case为止。

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

Copyright 2002 Chinaxp. All rights reserved 18

TDD 和 PSP

Personal Software Process 的 Development

Design Code Build Test

Test-Driven Design and Development

Analysis Code Unit Test Code Build Run Test

Analysis

Design

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

Copyright 2002 Chinaxp. All rights reserved 19

XP采用了 TDDTDD 是 Extreme Programming 中必须遵行的一个方法。 TDD 是 XP 中

Pair

Programming 的工作模式。• XP中把测试驱动的设计和开发做到极致。 TDD 的整个流程由两个程

序员一起执行。• XP正是因为采用了 TDD 才能够做到每天的代码都是 Production Cod

e 和每个小的 Release 都能提供具备 Production 质量的代码并投入使用。

• 有了 TDD,XP 才能降低风险,去拥抱变化。• 有了 TDD,XP 才能在计划的时间内完成计划质量的代码。• 有了 TDD,XP 才能减少 Code<->Fix环节,从而减少项目成本。• 有了 TDD,XP Team 才能对自己的工作充满自信。

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

Copyright 2002 Chinaxp. All rights reserved 20

TDD防止 Over-Engineering

在开发中采用 TDD ,可以有效的避免过度设计和开发。如果程序员不愿为一个 Method 写测试例子或者认为现在没有必要测试改Metho

d,那这个 Method多半是现在不需要的。

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

Copyright 2002 Chinaxp. All rights reserved 21

TDD, 程序员和管理层

对程序员来说,通过运行 Unit Test 和 Functional Test ,每天下班的时

候都可以清楚的知道自己的代码是 work 的。

对管理层来说,通过 Nightly Test 的结果,每天一早都清楚的知道项目的质量和开发进度。

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

Copyright 2002 Chinaxp. All rights reserved 22

XP 中谁来写 Tests

Developer:• Unit Test• Acceptance Test( Functional Test )

Customer:• Acceptance Test

Customer为每一个 User Story 写 Functional Test 。但通常用户并不

具备设计和开发 Functional Test 的能力,需要程序员的帮助。

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

Copyright 2002 Chinaxp. All rights reserved 23

什么时候写 Tests?

• 如果你要写一个新的功能,请先写她的测试例子• 如果你要在没有经过测试的代码上写新的功能,请先写目前代码

的测试例子• 如果你要 Fix 一个 Bug ,请先为这个 Bug 写一个测试例子• 如果你要 Refactor没有测试过的代码,请先写一个测试例子• 如果你发现一个边缘例外值,请为她写一个测试例子

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

Copyright 2002 Chinaxp. All rights reserved 24

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

……

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

Copyright 2002 Chinaxp. All rights reserved 25

Junit( A sample)

Junit 是由 kent Beck 和 Erich Gamma 编写的一个 open source 的测试框架,用来编写可重复的测试例子。

测试论坛中的增加用户 methodpublic 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,

AccountAlreadyExistE

xception{……}

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

Copyright 2002 Chinaxp. All rights reserved 26

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);

}

}

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

Copyright 2002 Chinaxp. All rights reserved 27

Junit( A sample)

2. Override setUp() and tearDown()如果需要,可以在 setUp() 中初始化需要的全局变量,资源等(比如

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();

}

}

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

Copyright 2002 Chinaxp. All rights reserved 28

Junit( A sample)3. 为被测试的 Method 写 Test Case……public void testAddAccountNormal(){

AccountDAOmySql dao = new AccountDAOmySql();Account account = new Account(USER_NAME,"charles","[email protected]");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 [email protected]",account.getEmail(),account_new.getEmail

() ); dao.removeAccount( account.getUserName() ); }catch( final Exception e ){ e.printStackTrace(); fail("Unexpected exception::" + e.toString()); }}

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

Copyright 2002 Chinaxp. All rights reserved 29

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 ){// Passtry{dao.removeAccount( account.getUserName() );}catch( final SQLException sql ){

sql.printStackTrace();fail("Unexpected exception ");

}}

}

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

Copyright 2002 Chinaxp. All rights reserved 30

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.swing

ui.TestRunner org.redsoft.forum.dao.mysql.AccountDAOmySqlTest

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

Copyright 2002 Chinaxp. All rights reserved 31

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

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

Copyright 2002 Chinaxp. All rights reserved 32

Junit Test Suite

Test Suite 用来运行所有的 Unit TestsTest 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

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

Copyright 2002 Chinaxp. All rights reserved 33

Junit Test Suite

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

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

Copyright 2002 Chinaxp. All rights reserved 34

JFC Unit

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

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

Copyright 2002 Chinaxp. All rights reserved 35

JFC Unit代码片断:设置测试环境

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.cleanU

p( this ); super.tearDown( ); }

代码片断:测试图形界面JDialog dialog;JButton exitButton = ( JButton ) helper.findNamedComponent( "ExitButton",

loginScreen, 0 ); assertNotNull( "Could not find the Exit button", exitButton ); JButton enterButton = ( JButton ) helper.findNamedComponent( "EnterButton",

loginScreen, 0 ); assertNotNull( "Could not find the Enter button", enterButton );JTextField userNameField = ( JTextField ) helper.findNamedComponent( "LoginNameTextField",

loginScreen, 0 ); assertNotNull( "Could not find the userNameField", userNameField ); assertEquals( "Username field is empty", "", userNameField.getText( ) ); JTextField passwordField = ( JTextField ) helper.findNamedComponent( "PasswordTextField",

loginScreen, 0 ); assertNotNull( "Could not find the passwordField", passwordField ); assertEquals( "Password field is empty", "", passwordField.getText( ) );

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

Copyright 2002 Chinaxp. All rights reserved 36

利用 Ant 来做 Nightly Test

使用 Ant 中的两个 Tasks 来完成自动运行 Nightly Test<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>

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

Copyright 2002 Chinaxp. All rights reserved 37

测试例子覆盖率

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

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

Copyright 2002 Chinaxp. All rights reserved 38

Unit Tests : 100% always

任何时候如果 Unit Tests 的出现错误( Junit 的进度指示显示红色),XP Team 的首要工作就是修补 Unit Tests直至 Junit 的进度指示为绿色。

如果不修复出错的测试例子,就会出现滚雪球效应,在未知质量代码基础上的开发只会导致更多的未知质量的代码。软件质量的基石就开始崩溃。

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

Copyright 2002 Chinaxp. All rights reserved 39

Software Quality Management

• 你的项目有 Software Quality Management 吗?• 如何衡量? CMM Level4

-- 软件产品的质量管理是被预先计划的 -- 测量方法和目标是被预先定义和计划的 -- 质量管理的进度是被预先计划的

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

Copyright 2002 Chinaxp. All rights reserved 40

Reference

• Kent Beck,Extreme Programming Explained:Embrace Change

• C2.com

• Junit.org

• www.chinaxp.org