骆昊的技术专栏

什么是测试驱动开发

测试驱动开发是指在编写实现代码之前先写测试代码的开发方式。JUnit的作者Kent Beck说过:编写测试驱动代码的重要原因是消除开发中的恐惧和不确定性,因为编写代码时的恐惧会让你小心试探,,让你回避沟通,让你羞于得到反馈,让你变得焦躁不安,而TDD是消除恐惧、让Java开发者更加自信更加乐于沟通的重要手段。TDD会带来的好处可能不会马上呈现,但是你在某个时候一定会发现,这些好处包括:

TDD可以在多个测试级别上使用,如下表所示:

测试级别 描述

单元测试 测试类中的代码

集成测试 测试类之间的交互

系统测试 测试运行中的系统

系统集成测试 测试运行中的系统包括第三方组件

测试驱动开发的例子

现在我们需要一段代码来计算某个电影放映厅的门票收入,当前的业务规则非常简单,包括:

这里还有一个假设:目前因为没有专业的设备或系统来统计门票销售的数量,在计算门票收入时,门票销售数量是由使用者手动录入的。 TDD的基本步骤是:红色-绿色-重构。

接下来我们按照上述步骤完成门票收入计算的功能。

package com.lovo;import java.math.BigDecimal;import org.junit.Assert;import org.junit.Before;import org.junit.Test;{private TicketRevenue ticketRevenue;private BigDecimal expectedRevenue;() {ticketRevenue= new TicketRevenue();}() {expectedRevenue = new BigDecimal(“30”);Assert.assertEquals(expectedRevenue, ticketRevenue.estimateTotalRevenue(1));}}

上述测试代码不仅不能通过测试,甚至连编译都无法通过,因为TicketRevenue类还不存在呢。接下来我们可以利用IDE的代码修复功能(Eclipse和IntelliJ都有这样的功能)创建出TicketRevenue类以及该类中计算门票收入的estimateTotalRevenue方法。

package com.lovo;import java.math.BigDecimal;{public BigDecimal estimateTotalRevenue(int i) {return BigDecimal.ZERO;}}

现在可以运行你的单元测试用例了,但是由于我们还没有实现真正的业务逻辑,这个测试是不可能通过的,如下图所示。

但是,迄今为止我们已经完成了“红色“这个步骤。接下来我们修改TicketRevenue类的estimateTotalRevenue方法来让测试通过。

package com.lovo;import java.math.BigDecimal;{public BigDecimal estimateTotalRevenue(int numberOfTicketsSold) {BigDecimal totalRevenue = BigDecimal.ZERO;if(numberOfTicketsSold == 1) {totalRevenue = new BigDecimal(30);}return totalRevenue;}}

再次运行单元测试,结果如下图所示。

到这里,第二个步骤”绿色“就完成了。 接下来我们开始重构TicketRevenue类的代码。

package com.lovo;import java.math.BigDecimal;{TICKET_PRICE = 30;public BigDecimal estimateTotalRevenue(int numberOfTicketsSold) {BigDecimal totalRevenue = null;totalRevenue = new BigDecimal(TICKET_PRICE * numberOfTicketsSold);return totalRevenue;}}

重构后的代码可以根据输入的门票销售数量计算出对应的收入,较之之前的硬代码(hard code)它已经前进了一大步,但是很明显它没有考虑到输入小于0或者大于100的情况。因此我们需要更多的测试例来模拟实际工作环境中可能的输入,我们对刚才的测试代码进行了如下改进。

package com.lovo;import java.math.BigDecimal;import org.junit.Assert;import org.junit.Before;import org.junit.Test;{private TicketRevenue ticketRevenue;private BigDecimal expectedRevenue;() {ticketRevenue = new TicketRevenue();}@Test(expected = IllegalArgumentException.class)() {ticketRevenue.estimateTotalRevenue(-1);}() {Assert.assertEquals(BigDecimal.ZERO, ticketRevenue.estimateTotalRevenue(0));}() {expectedRevenue = new BigDecimal(“30”);Assert.assertEquals(expectedRevenue, ticketRevenue.estimateTotalRevenue(1));}() {expectedRevenue = new BigDecimal(“300”);Assert.assertEquals(expectedRevenue, ticketRevenue.estimateTotalRevenue(10));}@Test(expected = IllegalArgumentException.class)() {ticketRevenue.estimateTotalRevenue(101);}}

再次运行测试会发现5个测试中有两个无效输入的测试没有通过(销售数量为-1和101的测试),原因很简单,我们的代码中还没有处理无效输入的代码。接下来继续重构我们的代码。

package com.lovo;import java.math.BigDecimal;{TICKET_PRICE = 30;public BigDecimal estimateTotalRevenue(int numberOfTicketsSold)throws IllegalArgumentException {BigDecimal totalRevenue = null;if(numberOfTicketsSold < 0) {throw new IllegalArgumentException(“门票销售数量必须大于等于0”);}else if(numberOfTicketsSold > 100) {throw new IllegalArgumentException(“门票销售数量必须小于等于100”);}else {totalRevenue = new BigDecimal(TICKET_PRICE * numberOfTicketsSold);}return totalRevenue;}}与那些新人和旧人们共同经历吧!

骆昊的技术专栏

相关文章:

你感兴趣的文章:

标签云: