在測試用例中通過setUp()、tearDown()創建測試固件,只能使這個測試固件在單個測試用例的不同測試方法中共用,如果有多個測試用例都需要使用相同的測試固件,就需要將測試固件抽取到一個獨立的類中。JBuilder提供了3個預定義的測試固件類,它們分別是:
·JDBC測試固件(JDBC Fixture):用於獲取數據庫連接的測試固件,用戶僅需要通過設置一些數據庫信息,就可以用方便的方法獲取數據連接。
·JNDI 測試固件(JNDI Fixture):用於模擬從JDNI環境中獲取對象的測試固件。
·比較測試固件(Comparision Fixture):將測試輸出到外部文件中,以便第二次測試時進行比較。
·自定義測試固件(Custom Fixture):用戶自定義的測試固件。
如果對JUnit的框架結構非常了解,也許這些JBuilder擴展的測試固件對你來說意義並不大,它們無非是構建一些常見的測試環境罷了,你完全可以自己編寫。在本節裡,我們介紹兩個測試固件。
JDBC測試固件
如果你的工程中已經有一個獲取數據連接的公共類,你也有必要構建一個JDBC測試固件,因為JDBC測試固件不但可以直接通過創建測試用例的向導直接指定,此外,JDBC測試固件還提供了許多面向測試的方法。
為了創建JDBC測試固件,我們先創建一個JDataStore的數據庫,其數據文件位於<chapter25工程目錄>/db/hr.jds,這個數據庫的用戶名和密碼是:sysdba/123456。hr.jds數據庫中有一張EMPLOYEE的表,其結構如下所示:
圖 錯誤!文檔中沒有指定樣式的文字。EMPLOYEE表的數據
EMPLOYEE有3個字段,分別是ID、NAME和AGE,分別是String、String和int類型,並按上圖所示填入3條記錄。
為了演示JDBC測試固件的具體使用,我們設計兩個業務類:Employee和EmployeeDAO,爾後用JDBC測試固件為測試EmployeeDAO提供數據連接。這兩個類的代碼如下:
代碼清單 錯誤!文檔中沒有指定樣式的文字。Employee.java類
1.package chapter25.db;
2.public class Employee
3.{
4.private String id;
5.private String name;
6.private int age;
7.public Employee(String id, String name, int age) {
8.this.id = id;
9.this.name = name;
10.this.age = age;
11.}
12.public String getId() {
13.return id;
14.}
15.public String getName() {
16.return name;
17.}
18.public int getAge() {
19.return age;
20.}
21.public boolean equals(Object o) {
22.if (o instanceof Employee) {
23.Employee e1 = (Employee) o;
24.return id.equals(e1.getId()) && name.equals(e1.getName()) &&age == e1.getAge();
25.} else {
26.return false;
27.}
28.}
29.}
Employee類用於描述EMPLOYEE表的一條記錄,該類訪問數據庫的EmployeeDAO代碼如下所示:
代碼清單 錯誤!文檔中沒有指定樣式的文字。EmployeeDAO.java類
1.package chapter25.db;
2.import java.sql.*;
3.public class EmployeeDAO
4.{
5.private Connection conn;
6.public EmployeeDAO(Connection conn) {
7.this.conn = conn;
8.}
9.public Employee findById(String id) throws SQLException
10.{
11.String sqlStr = "select * from employee where id ='"+id+"'";
12.Statement stat = conn.createStatement();
13.ResultSet rs = stat.executeQuery(sqlStr);
14.if (rs.next()) {
15.return new Employee(id,rs.getString("name"),rs.getInt("age"));
16.}else{
17.return null;
18.}
19.}
20.}
為了節省篇幅,我們僅提供一個訪問數據庫的訪問方法:findById(),即通過id查找Employee對象。
下面,我們利用JBuilder向導創建一個JDBC測試固件:
1. File->New...->Test->在Test頁中,雙擊JDBC Fixture圖標,啟動創建向導,其對話框如下所示:
圖 錯誤!文檔中沒有指定樣式的文字。指定JDBC測試固件類名
在Class name中為JDBC測試固件指定類名:HrJdbcFixture,接受其他的默認設置,按Next到下一步。
2.設置連接數據庫的信息。
在這步裡,JBuilder提供了大部分數據庫驅動程序的選擇和連接信息設置,其對話框如下所示:
圖 錯誤!文檔中沒有指定樣式的文字。指定數據庫連接信息
·Driver:選擇borland.databstore.jdbc.DataStoreDriver類。JDBC測試固件提供了對大多數數據庫的支持。其中下拉框中暫不可用的數據庫驅動器類顯示為紅色,你可以通過配置工程擴展類庫使它們可用。
·URL:點擊其後的…按鈕,彈出Create URL for DataStore對話框,如下圖所示:
圖 錯誤!文檔中沒有指定樣式的文字。構造DataStore數據連接URL的對話框
該對話框的設置內容會隨著數據庫類型的不同而變化。對於JDataStore數據庫,該對話框提供了兩個選項,如果數據庫文件放在本機上時用第一個設置項,否則用第二個設置項。我們選擇第一種選項,點擊其後的…按鈕,導航到<chapter25工程目錄>/db/hr.jds並選擇之,按OK返回向導主對話框窗口。
·User name:sysdba。
·Password:123456。
按對話框下的Test Connection測試連接,應該會返回一個Success信息報告連接測試成功。按Finish創建JDBC 測試固件,其代碼如下所示:
代碼清單 錯誤!文檔中沒有指定樣式的文字。HrJdbcFixture.java
1.package fixture;
2.import java.sql.*;
3.import java.io.*;
4.import com.borland.jbuilder.unittest.JdbcFixture;
5.public class HrJdbcFixture
6.extends JdbcFixture
7.{
8.public HrJdbcFixture(Object obj) {
9.super();
10.super.setUrl("jdbc:borland:dslocal:D:/JTJB2005/chapter25/db/hr.jds");
11.super.setDriver("com.borland.datastore.jdbc.DataStoreDriver");
12.super.setUsername("sysdba");
13.super.setPassword("123456");
14.}
15.
16.public void setUp() {
17.super.setUp();
18.}
19.
20.public void tearDown() {
21.super.tearDown();
22.}
23.}
JDBC測試固件承繼了com.borland.jbuilder.unittest.JdbcFixture,這個類的重要方法包括:
·dumpResultSet():將一個ResultSet導到一個Writer中,該方法接受兩個參數,一個是ResultSet另一個是Writer。
·getConnection():獲取一個數據連接。
·runSqlBuffer():執行緩存於StringBuffer對象中的SQL語句。
·runSqlFile():執行保存在文件中的SQL語句,通過入參指定SQL文件的地址。
·setDriver():設置JDBC驅動器。
·setUrl():設置數據連接的URL。
·setUsername():設置用戶名。
·setPassword():設置密碼。
提示:
通過向導創建JDataStore的JDBC測試固件,雖然可以直接在對話框中選擇com.borland.datastore.jdbc.DataStoreDriver驅動器,但運行這個JDBC測試固件時,JBuilder卻報這樣的錯誤信息:java.lang.ClassNotFoundException: com.borland.datastore.jdbc.DataStoreDriver。原來是JBuilder通過向導創建JDBC測試固件時,並沒有直接將驅動器類加載到工程類庫中,所以你需要手工通過Project->Project Properties...->Paths中,將JBuilder類庫中名為JDataStore類庫項加到工程類庫中。
由於EMPLOYEE表的數據可能會隨著測試的進行不斷更改,這樣在測試時測試規則就很難制定,因為規則的制定必須基於一個假設的環境。舉個例子,我們現在要測試findById()方法,就必須知道EMPLOYEE表中有哪些數據,所以在測試開始時就必須創建好一些特定的數據。由於JDBC固件可以執行保存在外部文件中的SQL,所以我們創建一個insert.sql文件,將其放置在<chapter25工程目錄>/db/insert.sql下,文件的內容如下:
delete from employee;insert into employee values('0004','大山',23);insert into employee values('0005','英華',30);insert into employee values('0006','柯明',31);
運行這個SQL語句時,先清空EMPLOYEE表中的數據,然後再插入3條特定的記錄。下面,我們來創建應用JDBC測試固件的TestEmployeeDAO測試用例類。
1.在編輯器中激活EmployeeDAO。
2.File->New...->Test->雙擊Test Case圖標啟動創建測試用例的向導,在向導第1、2步為EmployeeDAO的創建一個名為TestEmployeeDAO的測試用例,這個測試用例對EmployeeDAO的findById()方法進行功能測試。
3.在向導的第3步選擇測試固件,在向導對話框中我們前面創建的HrJdbcFixture已經出現在列表中。你也可以通過對話框的Add...和Remove選擇不同的測試固件。
圖 錯誤!文檔中沒有指定樣式的文字。選擇測試固件
按Finish直接創建TestEmployeeDAO的測試用例,其代碼如下所示:
代碼清單 錯誤!文檔中沒有指定樣式的文字。TestEmployeeDAO.java,向導創建的測試用例類
1.package chapter25.db;
2.import junit.framework.*;
3.import fixture.*;
4.import java.sql.*;
5.public class TestEmployeeDAO extends TestCase {
6.private EmployeeDAO employeeDAO = null;
7.HrJdbcFixture hrJdbcFixture;
8.protected void setUp() throws Exception {
9.super.setUp();
10./**@todo verify the constructors*/
11.employeeDAO = new EmployeeDAO(null);
12.hrJdbcFixture = new HrJdbcFixture(this);
13.hrJdbcFixture.setUp();
14.}
15.
16.protected void tearDown() throws Exception {
17.employeeDAO = null;
18.hrJdbcFixture.tearDown();
19.hrJdbcFixture = null;
20.super.tearDown();
21.}
22.
23.public void testFindById() throws SQLException {
24.String id = "";
25.Employee expectedReturn = null;
26.Employee actualReturn = employeeDAO.findById(id);
27.assertEquals("return value", expectedReturn, actualReturn);
28./**@todo fill in the test code*/
29.}
30.}
測試用例在setUp()方法中實例化HrJdbcFixture對象,並調用其setUp()方法初始化環境。只有JDBC測試固件的setUp()方法執行後(第13),才可調用JDBC測試固件的其他方法,如getConnection()等,所以JDBC測試固件的setUp()是其初始化方法。下面我們對這個TestEmployeeDAO進行改造,改造的代碼如下粗體代碼所示:
代碼清單 錯誤!文檔中沒有指定樣式的文字。改造後的TestEmployeeDAO類
1.…
2.public class TestEmployeeDAO extends TestCase {
3.…
4.protected void setUp() throws Exception {
5.super.setUp();
6.hrJdbcFixture = new HrJdbcFixture(this);
7.hrJdbcFixture.setUp();
8.employeeDAO = new EmployeeDAO(hrJdbcFixture.getConnection());
9.hrJdbcFixture.runSqlFile("D:/JTJB2005/chapter25/db/insert.sql",true);
10.}
11.…
12.public void testFindById() throws SQLException {
13.Employee expectEmp = new Employee("0004","大山",23);
14.Employee realEmp = employeeDAO.findById("0004");
15.assertNotNull(realEmp);
16.assertEquals(expectEmp,realEmp);
17.}
18.}
因為JDBC測試固件需要在setUp()方法調用後,其他方法才可用,所以在TestEmployeeDAO的setUp()方法中,我們將EmployeeDAO的實例化方法移到後面,以便實例化EmployeeDAO時可以通過hrJdbcFixture.getConnection()獲取數據連接(第8行)。在第9行,執行insert.sql文件,清除表中原來的數據並插入3行測試數據。
在第13~14行,通過EmployeeDAO的findById()方法查找返回ID為0004的Employee對象,在第15~16行設定兩個測試規則。
運行這個帶JDBC測試固件的測試用例,HrJbdcFixture測試固件先准備好測試環境,然後再執行TestEmployeeDAO的testFindById()測試方法。
比較測試固件
比較固件用於記錄下當前的測試記錄,以便和下一次的輸出比較。比較固件類繼承於com.borland.jbuilder.unittest.TestRecorder,而TestRecorder類繼承java.io.Writer。所以如果在測試時,需要用Writer輸出信息就可以考慮使用比較固件了,它提供了許多易用的輸出信息的方法。你可以通過向導來創建比較固件。
TestRecorder共有 4個記錄模式的常量,它們分別是:
·UPDATE:比較固件將當前輸出信息和已存在的信息文件相比較,如果文件沒有存在則新創建一個文件,記錄輸出信息。
·COMPARE:比較固件將當前輸出的信息和已經存在的信息比較。
·RECORD:比較固件記錄當前輸出的信息,如果原來已經有輸出文件存在,覆蓋之。
·OFF:關閉比較固件的功能。
注意:
在創建記錄文件後,假設你更改了測試用例或測試套件,需要重新初始化這個輸出文件:將TestRecorder的輸出模式設置為RECORD,創建文件後再將其調整為UPDATE。輸出的數據文件是二進制文件,放在和源程序文件相同的目錄下且和測試用例類同名。
下面是測試固件常用的方法,介紹如下:
·boolean print(String s)
用TestRecorder打印一個字符串,如果模式為RECORD,且這個字符串和原來記錄的不一致,則返回false。你可以設定這樣的測試規則:
assertTrue(recorder.print(result.toString())
·boolean println(String s)
和print()相似,只不過添加一個換行。
·boolean compareObject(Object o)
調用傳入對象的equals()方法和用前面用recordObject()記錄的對象進行比較。
·boolean recordObject()
記錄一個對象,以便後面調用compareObject()方法進行比較。
下面,我們創建一個比較固件,並應用這個比較固件為Employee類創建一個測試用例。
1.File->New...->Test->在Test頁中,雙擊Comparision Fixture圖標啟動創建比較固件類的向導,其對話框如下所示:
圖 錯誤!文檔中沒有指定樣式的文字。指定比較固件名及屬性
·Class name:測試固件類名,接受默認的ComparisionFixture1。
·Echo output to console:測試固件將信息同時輸出到測試運行器的控制台上。
·Verbose output:測試固件將輸出詳細的信息。
此外,Save comparision data in this directory指定比較輸出信息文件的存放位置,可以通過其後的…按鈕更改,這裡我們接受默認的設置。按OK直接創建比較固件類,你代碼如下所示:
代碼清單 錯誤!文檔中沒有指定樣式的文字。?15 ComparisonFixture1.java,向導創建的測試固件類
1.package fixture;
2.import com.borland.jbuilder.unittest.TestRecorder;
3.public class ComparisonFixture1 extends TestRecorder
4.{
5.
6.public ComparisonFixture1(Object obj) {
7.super();
8.super.setMode(UPDATE);
9.super.setVerbose(true);
10.super.setEcho(true);
11.String fileName = super.constructFilename("D:/JTJB2005/chapter25/test",obj);
12.super.setOutputFile(fileName);
13.}
14.
15.public void setUp() {
16.}
17.
18.public void tearDown() {
19.}
20.}
第8行將模式設置為UPDATE,而第9,10行對輸出屬性作設置。第11~12行指定輸出文件的目錄。
2.創建TestEmployee測試用例類。
File->New...->Test->在Test頁,雙擊Test Case圖標啟動創建測試用例向導,為Employee類和構造函數創建TestEmployee測試用例類。在向導的第1步,你將看到如下的對話框:
圖 錯誤!文檔中沒有指定樣式的文字。選擇測試Employee類的構造函數
點擊Next一直到向導的第3步:
列表中列出了工程的所有的測試固件,選擇HrJdbcFixture,點擊Remove刪除這個固件,只留下ComparisionFixture1的固件,點擊Finish直接創建TestEmployee測試用例類的代碼框架,在代碼框架基礎上利用比較固件對Employee進行測試,其最終代碼如下所示:
代碼清單 錯誤!文檔中沒有指定樣式的文字。應用比較固件的測試用例
1.package chapter25.db;
2.
3.import junit.framework.*;
4.import fixture.*;
5.
6.public class TestEmployee extends TestCase {
7.private Employee employee = null;
8.ComparisonFixture1 comparisonFixture1;
9.
10.protected void setUp() throws Exception {
11.super.setUp();
12.employee = new Employee("0004", "王五", 23);
13.comparisonFixture1 = new ComparisonFixture1(this);
14.comparisonFixture1.setUp();
15.}
16.
17.protected void tearDown() throws Exception {
18.employee = null;
19.comparisonFixture1.tearDown();
20.comparisonFixture1 = null;
21.super.tearDown();
22.}
23.
24.public void testEmployee() {
25.String id = "0004";
26.String name = "王五";
27.int age = 23;
28.comparisonFixture1.print(employee.getId());
29.comparisonFixture1.recordObject(employee);
30.employee = new Employee(id, name, age);
31.assertTrue(comparisonFixture1.print(employee.getId()));
32.assertTrue(comparisonFixture1.compareObject(employee));
33.}
34.}
在第12行實例化一個Employee對象,在第28行保持並打印出原employee對象的id值,在第31行進行比較;第29行記錄原employee對象,在第31行進行兩對象的比較。
運行TestEmployee類,在測試運行器的測試輸出標簽頁中,你將可以看到輸出的信息,在測試用例所在的文件夾下將創建一個無後綴名的輸出文件Employee。