软件代码在一些方面很像艺术品,各家的审美标准不同,对同一段代码的看法也就不一样。虽然有很多公认的设计模式和原则,但在具体应用上有时也是公说公有理,婆说婆有理。这里我分享一些通过编写测试得出的度量项目,通过测试代码中体现的问题反过来检查产品代码的问题。
一、枚举的规则意味着枚举的测试
Java代码
if (CARREFOUR.equals(supplier)) { return EUR; } else if (CENTURY_MART.equals(supplier)) { return CNY; } else { throw new UnsupportedSupplierException(supplier); } |
类似这样的条件分支语句非常常见,其测试也并不难,将每种条件都覆盖到即可
Java代码
@Test public void returnsEurWhenGetCurrenyGivenCarrefour() throws Exception { assertEquals(EUR, target.getCurrencyBy(CARREFOUR)); } @Test public void returnsCnyWhenGetCurrenyGivenCenturyMart() throws Exception { assertEquals(CNY, target.getCurrencyBy(CENTURY_MART)); } @Test(expected=UnsupportedSupplierException.class) public void throwsExceptionWhenGetCurrenyGivenUnknownSupplier() throws Exception { target.getCurrencyBy(WE_DONT_KNOW); } |
如果现在要支持一个新的供应商,那么可以增加一个测试用例,然后在条件分支中增加一个else if来实现,也就是说每新增一个供应商,我们就得新增一个测试,如果这种变化的频率比较高(噢,这是好事,说明生意做得不赖),代码和测试就会显得比较笨拙。让我们改进一下代码:
Java代码
final String currency = currencies.get(supplier); if (currency != null) { return currency; } else { throw new UnsupportedSupplierException(supplier); } |
由于使用了Map作为实现,测试分支是固定的了,和供应商的增减无关了。
Java代码
@Before public void setupTestFixture() { Map<string, String> currencies = new HashMap<String, String>(); currencies.put(MATCHED, CORRESPONSED); currencies.put(ANOTHER, ANOTHER_CURRENCY); target.setCurrencies(currencies); } @Test public void returnsCorresponsedCurrencyWhenSupplierNameMatched() throws Exception { assertEquals(CORRESPONSED, target.getCurrencyBy(MATCHED)); } @Test(expected=UnsupportedSupplierException.class) public void throwsExceptionWhenGetCurrenyGivenUnknownSupplier() throws Exception { target.getCurrencyBy(WE_DONT_KNOW); } |