在測(cè)試用例中通過setUp()、tearDown()創(chuàng)建測(cè)試固件,只能使這個(gè)測(cè)試固件在單個(gè)測(cè)試用例的不同測(cè)試方法中共用,如果有多個(gè)測(cè)試用例都需要使用相同的測(cè)試固件,就需要將測(cè)試固件抽取到一個(gè)獨(dú)立的類中。JBuilder提供了3個(gè)預(yù)定義的測(cè)試固件類,它們分別是:
·JDBC測(cè)試固件(JDBC Fixture):用于獲取數(shù)據(jù)庫(kù)連接的測(cè)試固件,用戶僅需要通過設(shè)置一些數(shù)據(jù)庫(kù)信息,就可以用方便的方法獲取數(shù)據(jù)連接。
·JNDI 測(cè)試固件(JNDI Fixture):用于模擬從JDNI環(huán)境中獲取對(duì)象的測(cè)試固件。
·比較測(cè)試固件(Comparision Fixture):將測(cè)試輸出到外部文件中,以便第二次測(cè)試時(shí)進(jìn)行比較。
·自定義測(cè)試固件(Custom Fixture):用戶自定義的測(cè)試固件。
如果對(duì)JUnit的框架結(jié)構(gòu)非常了解,也許這些JBuilder擴(kuò)展的測(cè)試固件對(duì)你來說意義并不大,它們無非是構(gòu)建一些常見的測(cè)試環(huán)境罷了,你完全可以自己編寫。在本節(jié)里,我們介紹兩個(gè)測(cè)試固件。
JDBC測(cè)試固件
如果你的工程中已經(jīng)有一個(gè)獲取數(shù)據(jù)連接的公共類,你也有必要構(gòu)建一個(gè)JDBC測(cè)試固件,因?yàn)镴DBC測(cè)試固件不但可以直接通過創(chuàng)建測(cè)試用例的向?qū)е苯又付ǎ送猓琂DBC測(cè)試固件還提供了許多面向測(cè)試的方法。
為了創(chuàng)建JDBC測(cè)試固件,我們先創(chuàng)建一個(gè)JDataStore的數(shù)據(jù)庫(kù),其數(shù)據(jù)文件位于/db/hr.jds,這個(gè)數(shù)據(jù)庫(kù)的用戶名和密碼是:sysdba/123456。hr.jds數(shù)據(jù)庫(kù)中有一張EMPLOYEE的表,其結(jié)構(gòu)如下所示:
 圖 錯(cuò)誤!文檔中沒有指定樣式的文字。EMPLOYEE表的數(shù)據(jù) EMPLOYEE有3個(gè)字段,分別是ID、NAME和AGE,分別是String、String和int類型,并按上圖所示填入3條記錄。
為了演示JDBC測(cè)試固件的具體使用,我們?cè)O(shè)計(jì)兩個(gè)業(yè)務(wù)類:Employee和EmployeeDAO,爾后用JDBC測(cè)試固件為測(cè)試EmployeeDAO提供數(shù)據(jù)連接。這兩個(gè)類的代碼如下:
代碼清單 錯(cuò)誤!文檔中沒有指定樣式的文字。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表的一條記錄,該類訪問數(shù)據(jù)庫(kù)的EmployeeDAO代碼如下所示:
代碼清單 錯(cuò)誤!文檔中沒有指定樣式的文字。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. }
為了節(jié)省篇幅,我們僅提供一個(gè)訪問數(shù)據(jù)庫(kù)的訪問方法:findById(),即通過id查找Employee對(duì)象。
下面,我們利用JBuilder向?qū)?chuàng)建一個(gè)JDBC測(cè)試固件:
1. File->New...->Test->在Test頁(yè)中,雙擊JDBC Fixture圖標(biāo),啟動(dòng)創(chuàng)建向?qū)В鋵?duì)話框如下所示:  圖 錯(cuò)誤!文檔中沒有指定樣式的文字。指定JDBC測(cè)試固件類名 在Class name中為JDBC測(cè)試固件指定類名:HrJdbcFixture,接受其他的默認(rèn)設(shè)置,按Next到下一步。 2.設(shè)置連接數(shù)據(jù)庫(kù)的信息。
在這步里,JBuilder提供了大部分?jǐn)?shù)據(jù)庫(kù)驅(qū)動(dòng)程序的選擇和連接信息設(shè)置,其對(duì)話框如下所示: 
圖 錯(cuò)誤!文檔中沒有指定樣式的文字。指定數(shù)據(jù)庫(kù)連接信息 ·Driver:選擇borland.databstore.jdbc.DataStoreDriver類。JDBC測(cè)試固件提供了對(duì)大多數(shù)數(shù)據(jù)庫(kù)的支持。其中下拉框中暫不可用的數(shù)據(jù)庫(kù)驅(qū)動(dòng)器類顯示為紅色,你可以通過配置工程擴(kuò)展類庫(kù)使它們可用。
·URL:點(diǎn)擊其后的…按鈕,彈出Create URL for DataStore對(duì)話框,如下圖所示:  圖 錯(cuò)誤!文檔中沒有指定樣式的文字。構(gòu)造DataStore數(shù)據(jù)連接URL的對(duì)話框 該對(duì)話框的設(shè)置內(nèi)容會(huì)隨著數(shù)據(jù)庫(kù)類型的不同而變化。對(duì)于JDataStore數(shù)據(jù)庫(kù),該對(duì)話框提供了兩個(gè)選項(xiàng),如果數(shù)據(jù)庫(kù)文件放在本機(jī)上時(shí)用第一個(gè)設(shè)置項(xiàng),否則用第二個(gè)設(shè)置項(xiàng)。我們選擇第一種選項(xiàng),點(diǎn)擊其后的…按鈕,導(dǎo)航到/db/hr.jds并選擇之,按OK返回向?qū)е鲗?duì)話框窗口。
·User name:sysdba。
·Password:123456。
按對(duì)話框下的Test Connection測(cè)試連接,應(yīng)該會(huì)返回一個(gè)Success信息報(bào)告連接測(cè)試成功。按Finish創(chuàng)建JDBC 測(cè)試固件,其代碼如下所示:
代碼清單 錯(cuò)誤!文檔中沒有指定樣式的文字。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測(cè)試固件承繼了com.borland.jbuilder.unittest.JdbcFixture,這個(gè)類的重要方法包括:
·dumpResultSet():將一個(gè)ResultSet導(dǎo)到一個(gè)Writer中,該方法接受兩個(gè)參數(shù),一個(gè)是ResultSet另一個(gè)是Writer。
·getConnection():獲取一個(gè)數(shù)據(jù)連接。
·runSqlBuffer():執(zhí)行緩存于StringBuffer對(duì)象中的SQL語句。
·runSqlFile():執(zhí)行保存在文件中的SQL語句,通過入?yún)⒅付⊿QL文件的地址。
·setDriver():設(shè)置JDBC驅(qū)動(dòng)器。
·setUrl():設(shè)置數(shù)據(jù)連接的URL。
·setUsername():設(shè)置用戶名。
·setPassword():設(shè)置密碼。
提示:
通過向?qū)?chuàng)建JDataStore的JDBC測(cè)試固件,雖然可以直接在對(duì)話框中選擇com.borland.datastore.jdbc.DataStoreDriver驅(qū)動(dòng)器,但運(yùn)行這個(gè)JDBC測(cè)試固件時(shí),JBuilder卻報(bào)這樣的錯(cuò)誤信息:java.lang.ClassNotFoundException: com.borland.datastore.jdbc.DataStoreDriver。原來是JBuilder通過向?qū)?chuàng)建JDBC測(cè)試固件時(shí),并沒有直接將驅(qū)動(dòng)器類加載到工程類庫(kù)中,所以你需要手工通過Project->Project Properties...->Paths中,將JBuilder類庫(kù)中名為JDataStore類庫(kù)項(xiàng)加到工程類庫(kù)中。
由于EMPLOYEE表的數(shù)據(jù)可能會(huì)隨著測(cè)試的進(jìn)行不斷更改,這樣在測(cè)試時(shí)測(cè)試規(guī)則就很難制定,因?yàn)橐?guī)則的制定必須基于一個(gè)假設(shè)的環(huán)境。舉個(gè)例子,我們現(xiàn)在要測(cè)試findById()方法,就必須知道EMPLOYEE表中有哪些數(shù)據(jù),所以在測(cè)試開始時(shí)就必須創(chuàng)建好一些特定的數(shù)據(jù)。由于JDBC固件可以執(zhí)行保存在外部文件中的SQL,所以我們創(chuàng)建一個(gè)insert.sql文件,將其放置在/db/insert.sql下,文件的內(nèi)容如下:
delete from employee;insert into employee values('0004','大山',23);insert into employee values('0005','英華',30);insert into employee values('0006','柯明',31);
運(yùn)行這個(gè)SQL語句時(shí),先清空EMPLOYEE表中的數(shù)據(jù),然后再插入3條特定的記錄。下面,我們來創(chuàng)建應(yīng)用JDBC測(cè)試固件的TestEmployeeDAO測(cè)試用例類。
1.在編輯器中激活EmployeeDAO。
2.File->New...->Test->雙擊Test Case圖標(biāo)啟動(dòng)創(chuàng)建測(cè)試用例的向?qū)В谙驅(qū)У?、2步為EmployeeDAO的創(chuàng)建一個(gè)名為TestEmployeeDAO的測(cè)試用例,這個(gè)測(cè)試用例對(duì)EmployeeDAO的findById()方法進(jìn)行功能測(cè)試。
3.在向?qū)У牡?步選擇測(cè)試固件,在向?qū)?duì)話框中我們前面創(chuàng)建的HrJdbcFixture已經(jīng)出現(xiàn)在列表中。你也可以通過對(duì)話框的Add...和Remove選擇不同的測(cè)試固件。  圖 錯(cuò)誤!文檔中沒有指定樣式的文字。選擇測(cè)試固件 按Finish直接創(chuàng)建TestEmployeeDAO的測(cè)試用例,其代碼如下所示:
代碼清單 錯(cuò)誤!文檔中沒有指定樣式的文字。TestEmployeeDAO.java,向?qū)?chuàng)建的測(cè)試用例類
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. }
測(cè)試用例在setUp()方法中實(shí)例化HrJdbcFixture對(duì)象,并調(diào)用其setUp()方法初始化環(huán)境。只有JDBC測(cè)試固件的setUp()方法執(zhí)行后(第13),才可調(diào)用JDBC測(cè)試固件的其他方法,如getConnection()等,所以JDBC測(cè)試固件的setUp()是其初始化方法。下面我們對(duì)這個(gè)TestEmployeeDAO進(jìn)行改造,改造的代碼如下粗體代碼所示:
代碼清單 錯(cuò)誤!文檔中沒有指定樣式的文字。改造后的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. }
因?yàn)镴DBC測(cè)試固件需要在setUp()方法調(diào)用后,其他方法才可用,所以在TestEmployeeDAO的setUp()方法中,我們將EmployeeDAO的實(shí)例化方法移到后面,以便實(shí)例化EmployeeDAO時(shí)可以通過hrJdbcFixture.getConnection()獲取數(shù)據(jù)連接(第8行)。在第9行,執(zhí)行insert.sql文件,清除表中原來的數(shù)據(jù)并插入3行測(cè)試數(shù)據(jù)。
在第13~14行,通過EmployeeDAO的findById()方法查找返回ID為0004的Employee對(duì)象,在第15~16行設(shè)定兩個(gè)測(cè)試規(guī)則。
運(yùn)行這個(gè)帶JDBC測(cè)試固件的測(cè)試用例,HrJbdcFixture測(cè)試固件先準(zhǔn)備好測(cè)試環(huán)境,然后再執(zhí)行TestEmployeeDAO的testFindById()測(cè)試方法。 比較測(cè)試固件
比較固件用于記錄下當(dāng)前的測(cè)試記錄,以便和下一次的輸出比較。比較固件類繼承于com.borland.jbuilder.unittest.TestRecorder,而TestRecorder類繼承java.io.Writer。所以如果在測(cè)試時(shí),需要用Writer輸出信息就可以考慮使用比較固件了,它提供了許多易用的輸出信息的方法。你可以通過向?qū)韯?chuàng)建比較固件。
TestRecorder共有 4個(gè)記錄模式的常量,它們分別是:
·UPDATE:比較固件將當(dāng)前輸出信息和已存在的信息文件相比較,如果文件沒有存在則新創(chuàng)建一個(gè)文件,記錄輸出信息。
·COMPARE:比較固件將當(dāng)前輸出的信息和已經(jīng)存在的信息比較。
·RECORD:比較固件記錄當(dāng)前輸出的信息,如果原來已經(jīng)有輸出文件存在,覆蓋之。
·OFF:關(guān)閉比較固件的功能。
注意:
在創(chuàng)建記錄文件后,假設(shè)你更改了測(cè)試用例或測(cè)試套件,需要重新初始化這個(gè)輸出文件:將TestRecorder的輸出模式設(shè)置為RECORD,創(chuàng)建文件后再將其調(diào)整為UPDATE。輸出的數(shù)據(jù)文件是二進(jìn)制文件,放在和源程序文件相同的目錄下且和測(cè)試用例類同名。
下面是測(cè)試固件常用的方法,介紹如下:
·boolean print(String s)
用TestRecorder打印一個(gè)字符串,如果模式為RECORD,且這個(gè)字符串和原來記錄的不一致,則返回false。你可以設(shè)定這樣的測(cè)試規(guī)則:
assertTrue(recorder.print(result.toString())
·boolean println(String s)
和print()相似,只不過添加一個(gè)換行。
·boolean compareObject(Object o)
調(diào)用傳入對(duì)象的equals()方法和用前面用recordObject()記錄的對(duì)象進(jìn)行比較。
·boolean recordObject()
記錄一個(gè)對(duì)象,以便后面調(diào)用compareObject()方法進(jìn)行比較。
下面,我們創(chuàng)建一個(gè)比較固件,并應(yīng)用這個(gè)比較固件為Employee類創(chuàng)建一個(gè)測(cè)試用例。
1.File->New...->Test->在Test頁(yè)中,雙擊Comparision Fixture圖標(biāo)啟動(dòng)創(chuàng)建比較固件類的向?qū)В鋵?duì)話框如下所示:  圖 錯(cuò)誤!文檔中沒有指定樣式的文字。指定比較固件名及屬性 ·Class name:測(cè)試固件類名,接受默認(rèn)的ComparisionFixture1。
·Echo output to console:測(cè)試固件將信息同時(shí)輸出到測(cè)試運(yùn)行器的控制臺(tái)上。
·Verbose output:測(cè)試固件將輸出詳細(xì)的信息。
此外,Save comparision data in this directory指定比較輸出信息文件的存放位置,可以通過其后的…按鈕更改,這里我們接受默認(rèn)的設(shè)置。按OK直接創(chuàng)建比較固件類,你代碼如下所示:
代碼清單 錯(cuò)誤!文檔中沒有指定樣式的文字。?15 ComparisonFixture1.java,向?qū)?chuàng)建的測(cè)試固件類
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行將模式設(shè)置為UPDATE,而第9,10行對(duì)輸出屬性作設(shè)置。第11~12行指定輸出文件的目錄。
2.創(chuàng)建TestEmployee測(cè)試用例類。
File->New...->Test->在Test頁(yè),雙擊Test Case圖標(biāo)啟動(dòng)創(chuàng)建測(cè)試用例向?qū)В瑸镋mployee類和構(gòu)造函數(shù)創(chuàng)建TestEmployee測(cè)試用例類。在向?qū)У牡?步,你將看到如下的對(duì)話框:  圖 錯(cuò)誤!文檔中沒有指定樣式的文字。選擇測(cè)試Employee類的構(gòu)造函數(shù) 點(diǎn)擊Next一直到向?qū)У牡?步:
列表中列出了工程的所有的測(cè)試固件,選擇HrJdbcFixture,點(diǎn)擊Remove刪除這個(gè)固件,只留下ComparisionFixture1的固件,點(diǎn)擊Finish直接創(chuàng)建TestEmployee測(cè)試用例類的代碼框架,在代碼框架基礎(chǔ)上利用比較固件對(duì)Employee進(jìn)行測(cè)試,其最終代碼如下所示:  代碼清單 錯(cuò)誤!文檔中沒有指定樣式的文字。應(yīng)用比較固件的測(cè)試用例 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行實(shí)例化一個(gè)Employee對(duì)象,在第28行保持并打印出原employee對(duì)象的id值,在第31行進(jìn)行比較;第29行記錄原employee對(duì)象,在第31行進(jìn)行兩對(duì)象的比較。
運(yùn)行TestEmployee類,在測(cè)試運(yùn)行器的測(cè)試輸出標(biāo)簽頁(yè)中,你將可以看到輸出的信息,在測(cè)試用例所在的文件夾下將創(chuàng)建一個(gè)無后綴名的輸出文件Employee。
|