您的位置:軟件測試 > 開源軟件測試 > 開源單元測試工具 >
xUnit之一次測試代碼重構之旅
作者:ntop 發(fā)布時間:[ 2017/5/31 14:18:35 ] 推薦標簽:xUnit 單元測試

  現(xiàn)在代碼已經變得很簡潔了,由于消除了 try-finally 語句的緣故,也不需要提前聲明 billingAddress、customer ... 這些變量,所以代碼可以更簡潔:
public void testAddItemQuantity_severalQuantity_v9(){
//   Set up fixture
Address billingAddress = new Address("1222 1st St SW",
"Calgary", "Alberta", "T2N 2V2", "Canada");
registerTestObject(billingAddress);
Address shippingAddress = new Address("1333 1st St SW",
"Calgary", "Alberta", "T2N 2V2", "Canada");
registerTestObject(shippingAddress);
Customer customer = new Customer(99, "John", "Doe",
new BigDecimal("30"),
billingAddress,
shippingAddress);
registerTestObject(shippingAddress);
Product product = new Product(88, "SomeWidget",
new BigDecimal("19.99"));
registerTestObject(shippingAddress);
Invoice invoice = new Invoice(customer);
registerTestObject(shippingAddress);
// Exercise SUT
invoice.addItemQuantity(product, 5);
// Verify outcome
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.95"));
assertContainsExactlyOneLineItem(invoice, expected);
}
  構建測試環(huán)境部分 - Setup Fixture
  這部分代碼的特點是每個對象被創(chuàng)建完之后都會被注冊到框架中,一個簡單快速的重構方案是創(chuàng)建一個 createXXX 方法來做這些事情:先創(chuàng)建對象再注冊到框架中,額外的好處是如果對象的構造方法改變了,我們不需要去修改每個調用構造方法的地方:
public void testAddItemQuantity_severalQuantity_v10(){
//   Set up fixture
Address billingAddress =
createAddress( "1222 1st St SW", "Calgary", "Alberta",
"T2N 2V2", "Canada");
Address shippingAddress =
createAddress( "1333 1st St SW", "Calgary", "Alberta",
"T2N 2V2", "Canada");
Customer customer =
createCustomer( 99, "John", "Doe", new BigDecimal("30"),
billingAddress, shippingAddress);
Product product =
createProduct( 88,"SomeWidget",new BigDecimal("19.99"));
Invoice invoice = createInvoice(customer);
// Exercise SUT
invoice.addItemQuantity(product, 5);
// Verify outcome
LineItem expected =
new LineItem(invoice, product,5, new BigDecimal("30"),
new BigDecimal("69.96"));
assertContainsExactlyOneLineItem(invoice, expected);
}
  這段代碼依然有重構的空間:
  無法判斷輸入和輸出的關系,比如 Customer 的構造需要6個傳入?yún)?shù),但是我們無法判斷哪一個參數(shù)才是被驗證的,哪些參數(shù)是無關的,如果修改 Customer 的 Address 參數(shù)會不會影響測試結果,解決這個問題需要突出被測試的參數(shù)。
  使用硬編碼的測試數(shù)據(jù),在構建 Customer 時在代碼中寫死了6個參數(shù),每次執(zhí)行測試這6個參數(shù)都是不變的,假如每次構建Customer時需要將對象持久化在數(shù)據(jù)庫中,那么第二次執(zhí)行測試時會因為字段寫入沖突而失敗(假如Customer的name字段必須保持),所以每次執(zhí)行TestCase時對某些字段要隨機化每次都生成不一樣的字段。
  由于我們已經把對象的創(chuàng)建拿到了createXXX方法中,所以可以很容易做下面的重構:
public void testAddItemQuantity_severalQuantity_v11(){
final int QUANTITY = 5;
//   Set up fixture
Address billingAddress = createAnAddress();
Address shippingAddress = createAnAddress();
Customer customer = createACustomer(new BigDecimal("30"),
billingAddress, shippingAddress);
Product product = createAProduct(new BigDecimal("19.99"));
Invoice invoice = createInvoice(customer);
// Exercise SUT
invoice.addItemQuantity(product, QUANTITY);
// Verify outcome
LineItem expected =
new LineItem(invoice, product, 5, new BigDecimal("30"),
new BigDecimal("69.96"));
assertContainsExactlyOneLineItem(invoice, expected);
}
private Product createAProduct(BigDecimal unitPrice) {
BigDecimal uniqueId = getUniqueNumber();
String uniqueString = uniqueId.toString();
return new Product(uniqueId.toBigInteger().intValue(),
}
  這種模式叫 Anonymous Creation Method 模式,把不重要的字段放在構造方法內部實現(xiàn),把需要測試的字段通過方法的傳入?yún)?shù)暴露出來。同樣的道理,如果 Address 字段不影響測試,那么可以進一步的隱藏Address的構建:
public void testAddItemQuantity_severalQuantity_v12(){
//  Set up fixture
Customer cust = createACustomer(new BigDecimal("30"));
Product prod = createAProduct(new BigDecimal("19.99"));
Invoice invoice = createInvoice(cust);
// Exercise SUT
invoice.addItemQuantity(prod, 5);
// Verify outcome
LineItem expected = new LineItem(invoice, prod, 5,
new BigDecimal("30"), new BigDecimal("69.96"));
assertContainsExactlyOneLineItem(invoice, expected);
}
  后一個問題,我們使用了魔數(shù),這在學C語言的時候,老師已經強調過。需要把這些數(shù)字替換成更有意義的符號:
public void testAddItemQuantity_severalQuantity_v13(){
final int QUANTITY = 5;
final BigDecimal UNIT_PRICE = new BigDecimal("19.99");
final BigDecimal CUST_DISCOUNT_PC = new BigDecimal("30");
//   Set up fixture
Customer customer = createACustomer(CUST_DISCOUNT_PC);
Product product = createAProduct( UNIT_PRICE);
Invoice invoice = createInvoice(customer);
// Exercise SUT
invoice.addItemQuantity(product, QUANTITY);
// Verify outcome
final BigDecimal EXTENDED_PRICE = new BigDecimal("69.96");
LineItem expected =
new LineItem(invoice, product, QUANTITY,
CUST_DISCOUNT_PC, EXTENDED_PRICE);
assertContainsExactlyOneLineItem(invoice, expected);
}
  雖然如此“69.6”的出現(xiàn)依然是個問題,無法判斷這個數(shù)字是怎么得到的,如果它是通過某種計算得到的,應該在測試代碼中體現(xiàn)這種行為,所以這才是終版本的重構:
public void testAddItemQuantity_severalQuantity_v14(){
final int QUANTITY = 5;
final BigDecimal UNIT_PRICE = new BigDecimal("19.99");
final BigDecimal CUST_DISCOUNT_PC =  new BigDecimal("30");
// Set up fixture
Customer customer = createACustomer(CUST_DISCOUNT_PC);
Product product = createAProduct( UNIT_PRICE);
Invoice invoice = createInvoice(customer);
// Exercise SUT
invoice.addItemQuantity(product, QUANTITY);
// Verify outcome
final BigDecimal BASE_PRICE =
UNIT_PRICE.multiply(new BigDecimal(QUANTITY));
final BigDecimal EXTENDED_PRICE =
BASE_PRICE.subtract(BASE_PRICE.multiply(
CUST_DISCOUNT_PC.movePointLeft(2)));
LineItem expected =
createLineItem(QUANTITY, CUST_DISCOUNT_PC,
EXTENDED_PRICE, product, invoice);
assertContainsExactlyOneLineItem(invoice, expected);
}
  總結
  上面的重構把原來碩大的方法體修改到11行,測試代碼簡潔明了了很多。但是有時候我們不禁要問這樣的重構值得嗎?因為我們實際上是把更多的代碼變成工具方法轉移到了別的地方。如果僅僅只寫了這一個TestCase,這種重構略顯尷尬,如果還需要寫更多的TestCase,重構會讓之前的付出有所收獲,比如添加更多的TestCase時,只需要:
public void testAddLineItem_quantityOne(){
final BigDecimal BASE_PRICE = UNIT_PRICE;
final BigDecimal EXTENDED_PRICE = BASE_PRICE;
//   Set up fixture
Customer customer = createACustomer(NO_CUST_DISCOUNT);
Invoice invoice = createInvoice(customer);
//   Exercise SUT
invoice.addItemQuantity(PRODUCT, QUAN_ONE);
// Verify outcome
LineItem expected =
createLineItem( QUAN_ONE, NO_CUST_DISCOUNT,
EXTENDED_PRICE, PRODUCT, invoice);
assertContainsExactlyOneLineItem( invoice, expected );
}
public void testChangeQuantity_severalQuantity(){
final int ORIGINAL_QUANTITY = 3;
final int NEW_QUANTITY = 5;
final BigDecimal BASE_PRICE =
UNIT_PRICE.multiply(   new BigDecimal(NEW_QUANTITY));
final BigDecimal EXTENDED_PRICE =
BASE_PRICE.subtract(BASE_PRICE.multiply(
CUST_DISCOUNT_PC.movePointLeft(2)));
//   Set up fixture
Customer customer = createACustomer(CUST_DISCOUNT_PC);
Invoice invoice = createInvoice(customer);
Product product = createAProduct( UNIT_PRICE);
invoice.addItemQuantity(product, ORIGINAL_QUANTITY);
// Exercise SUT
invoice.changeQuantityForProduct(product, NEW_QUANTITY);
// Verify outcome
LineItem expected = createLineItem( NEW_QUANTITY,
CUST_DISCOUNT_PC, EXTENDED_PRICE, PRODUCT, invoice);
assertContainsExactlyOneLineItem( invoice, expected );
}

上一頁12下一頁
軟件測試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請使用列表 | 網站地圖
滬ICP備07036474 2003-2017 版權所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd