綠燈 | 紅燈

妙思資訊

2011年12月12日 星期一

你開發的程式好測試嗎(二)?


上一篇提到6種較不易進行測試的情況。接下來就來細說為何難測的原因及重構方法。

第一種情況:
 程式無聲無息的做完了,沒有提供任可訊息可供判斷!

這種情形,就好像太空船進到黑洞,完全無法回傳任何訊息一樣。無法取得訊息,就很難進行測試。當然還是能由一些間接方法來推測,但這樣做很容易因環境變更,而使得Unit Test 失敗。最好的方法還是程式能夠提供適當的訊息供Unit Test 做判斷。

假設有一支 ReportAgent,它可產生 Excel 和 PDF 報表。程式碼如下:

public class ReportAgent {

        //產生房客 Excel 報表資料 
 public boolean createExcelReport(Tenant tenant) throws IOException {

  if (tenant != null) {
   HSSFWorkbook wb = new HSSFWorkbook();
   HSSFSheet sheet = wb.createSheet();
   HSSFRow row = sheet.createRow(0);
   row.createCell(0).setCellValue(tenant.getName());
   row.createCell(1).setCellValue(tenant.getAge());
   row.createCell(2).setCellValue(tenant.getCareer());
   FileOutputStream out = new FileOutputStream("report.xls");
   wb.write(out);
   out.close();
   return true;
  } else {
   return false;
  }
 }

        //產生房客 PDF 報表資料 
 public boolean createPdfReport(Tenant tenant) throws FileNotFoundException,
   DocumentException {

  if (tenant != null) {
   Document document = new Document();
   PdfWriter.getInstance(document, new FileOutputStream("report.pdf"));
   document.open();
   document.add(new Paragraph("Name:"+tenant.getName()));
   document.add(new Paragraph("Age:"+tenant.getAge()));
   document.add(new Paragraph("Career:"+tenant.getCareer()));
   document.close();
   return true;
  } else {
   return false;
  }
 }
 
 //產生房客 Word 報表資料
 public boolean crateWordReport(Tenant tenant)  throws IOException{  
  //TODO
  return true;
 }

}
使用者需經由RentServiceImpl 才能產生房客報表。報表型態(ReportType)有三種,分別為Excel、PDF、WORD。但是房客報表只提供EXCEL和PDF二種報表的選擇,並不提供WORD報表。程式碼如下

public enum ReportType {

 EXCEL,PDF,WORD
}

public class RentServiceImpl implements RentService {

 private ReportAgent reportAgent;

 public void setReportAgent(ReportAgent reportAgent) {
  this.reportAgent = reportAgent;
 }

 @Override
 public void createReport(Tenant tenant, ReportType type)
   throws IOException, DocumentException {

  if (type == ReportType.EXCEL) {
   reportAgent.createExcelReport(tenant);
  } else if (type == ReportType.PDF) {
   reportAgent.createPdfReport(tenant);
  }
 }

}
當對 RentServiceImpl 中的createReport()進行測試時,無法得知是否執行正確。例如:當傳入的ReportType 為PDF 時,是否真的產生PDF報表,如果傳入的ReportType 為WORD,程式也不會有任何反應,所以這個測試只要不出Exception,所有的條件都會過,那這就不是一個有效的測試。測試碼如下

   public class RentServiceTest { 
 
 private RentServiceImpl service;
 
 @Before
 public void setUp(){
  
  service=new RentServiceImpl();
  service.setReportAgent(new ReportAgent());  
 } 
 
 //無效的測試,無法判別錯誤
 @Test
 public void testCreateReport() throws IOException, DocumentException{
  
  service.createReport(getTenant(), ReportType.PDF);
  service.createReport(getTenant(),  ReportType.WORD);
 } 
  
 private Tenant getTenant() {
  Tenant tenant=new Tenant(Identity.GENERAL);
  tenant.setName("Jim");
  tenant.setAge(30);
  tenant.setCareer("Doctor");
  return tenant;
 }

}
接下來,要來重構createReport(),當某個型態的report有被順利產出時,就回傳這個型號代號,若沒有這個型態的report就丟Exception。程式碼如下:

public ReportType createReport(Tenant tenant, ReportType type)
   throws IOException, DocumentException, ReportException {

  if (type == ReportType.EXCEL) {
   reportAgent.createExcelReport(tenant);
   return ReportType.EXCEL;
  } else if (type == ReportType.PDF) {
   reportAgent.createPdfReport(tenant);
   return ReportType.PDF;
  }else {
   throw new  ReportException("Format Not Support");
  }
 }

        @Test
 public void testCreateReport() throws IOException, DocumentException, ReportException{
  
  ReportType reportType=service.createReport(getTenant(), ReportType.PDF);
  Assert.assertEquals(ReportType.PDF, reportType);
  try {
   service.createReport(getTenant(),  ReportType.WORD);
   Assert.fail();
  } catch (Exception e) {
   
   Assert.assertTrue(true);
  }
 } 
這樣,測試程式可依回傳值來判斷是否有產生正確的Report。如果有傳入錯誤型態,程式也會以Exception 的方式告知。有了這些訊息,測試程式就能驗證邏輯是否正確。

沒有留言: