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

  現(xiàn)在代碼已經(jīng)變得很簡(jiǎn)潔了,由于消除了 try-finally 語(yǔ)句的緣故,也不需要提前聲明 billingAddress、customer ... 這些變量,所以代碼可以更簡(jiǎn)潔:
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);
}
  構(gòu)建測(cè)試環(huán)境部分 - Setup Fixture
  這部分代碼的特點(diǎn)是每個(gè)對(duì)象被創(chuàng)建完之后都會(huì)被注冊(cè)到框架中,一個(gè)簡(jiǎn)單快速的重構(gòu)方案是創(chuàng)建一個(gè) createXXX 方法來(lái)做這些事情:先創(chuàng)建對(duì)象再注冊(cè)到框架中,額外的好處是如果對(duì)象的構(gòu)造方法改變了,我們不需要去修改每個(gè)調(diào)用構(gòu)造方法的地方:
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);
}
  這段代碼依然有重構(gòu)的空間:
  無(wú)法判斷輸入和輸出的關(guān)系,比如 Customer 的構(gòu)造需要6個(gè)傳入?yún)?shù),但是我們無(wú)法判斷哪一個(gè)參數(shù)才是被驗(yàn)證的,哪些參數(shù)是無(wú)關(guān)的,如果修改 Customer 的 Address 參數(shù)會(huì)不會(huì)影響測(cè)試結(jié)果,解決這個(gè)問(wèn)題需要突出被測(cè)試的參數(shù)。
  使用硬編碼的測(cè)試數(shù)據(jù),在構(gòu)建 Customer 時(shí)在代碼中寫(xiě)死了6個(gè)參數(shù),每次執(zhí)行測(cè)試這6個(gè)參數(shù)都是不變的,假如每次構(gòu)建Customer時(shí)需要將對(duì)象持久化在數(shù)據(jù)庫(kù)中,那么第二次執(zhí)行測(cè)試時(shí)會(huì)因?yàn)樽侄螌?xiě)入沖突而失。偃鏑ustomer的name字段必須保持),所以每次執(zhí)行TestCase時(shí)對(duì)某些字段要隨機(jī)化每次都生成不一樣的字段。
  由于我們已經(jīng)把對(duì)象的創(chuàng)建拿到了createXXX方法中,所以可以很容易做下面的重構(gòu):
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 模式,把不重要的字段放在構(gòu)造方法內(nèi)部實(shí)現(xiàn),把需要測(cè)試的字段通過(guò)方法的傳入?yún)?shù)暴露出來(lái)。同樣的道理,如果 Address 字段不影響測(cè)試,那么可以進(jìn)一步的隱藏Address的構(gòu)建:
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);
}
  后一個(gè)問(wèn)題,我們使用了魔數(shù),這在學(xué)C語(yǔ)言的時(shí)候,老師已經(jīng)強(qiáng)調(diào)過(guò)。需要把這些數(shù)字替換成更有意義的符號(hào):
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)依然是個(gè)問(wèn)題,無(wú)法判斷這個(gè)數(shù)字是怎么得到的,如果它是通過(guò)某種計(jì)算得到的,應(yīng)該在測(cè)試代碼中體現(xiàn)這種行為,所以這才是終版本的重構(gòu):
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);
}
  總結(jié)
  上面的重構(gòu)把原來(lái)碩大的方法體修改到11行,測(cè)試代碼簡(jiǎn)潔明了了很多。但是有時(shí)候我們不禁要問(wèn)這樣的重構(gòu)值得嗎?因?yàn)槲覀儗?shí)際上是把更多的代碼變成工具方法轉(zhuǎn)移到了別的地方。如果僅僅只寫(xiě)了這一個(gè)TestCase,這種重構(gòu)略顯尷尬,如果還需要寫(xiě)更多的TestCase,重構(gòu)會(huì)讓之前的付出有所收獲,比如添加更多的TestCase時(shí),只需要:
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 );
}

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