首页 > 其他分享 >测试驱动开发之初窥门径

测试驱动开发之初窥门径

时间:2023-06-20 14:01:06浏览次数:20  
标签:BigDecimal 门径 测试 TicketRevenue import estimateTotalRevenue public 之初


什么是测试驱动开发

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

  1. 更清晰的代码 — 只写需要的代码
  2. 更好的设计
  3. 更出色的灵活性 — 鼓励程序员面向接口编程
  4. 更快速的反馈 — 不会到系统上线时才知道bug的存在

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

测试级别

描述

单元测试

测试类中的代码

集成测试

测试类之间的交互

系统测试

测试运行中的系统

系统集成测试

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

测试驱动开发的例子

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

  • 每张票售价(单价)¥30
  • 收入=门票销售数量*单价
  • 放映厅最多容纳100人

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

  1. 红色 - 编写无法通过的测试
  2. 绿色 - 编写实现代码并尽快让测试可以通过
  3. 重构 - 重构代码并再次让测试通过

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

package com.lovo;

import java.math.BigDecimal;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class TicketRevenueTest {
    private TicketRevenue ticketRevenue;
    private BigDecimal expectedRevenue;

    @Before
    public void setUp() {
        ticketRevenue= new TicketRevenue();
    }

    @Test
    public void oneTicketSoldIsThirtyInRevenue() {
        expectedRevenue = new BigDecimal("30");
        Assert.assertEquals(expectedRevenue,   ticketRevenue.estimateTotalRevenue(1));
    }
}

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

package com.lovo;

import java.math.BigDecimal;

public class TicketRevenue {

    public BigDecimal estimateTotalRevenue(int i) {
        return BigDecimal.ZERO;
    }

}

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

测试驱动开发之初窥门径_SOLID原则


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

package com.lovo;

import java.math.BigDecimal;

public class TicketRevenue {

    public BigDecimal estimateTotalRevenue(int numberOfTicketsSold) {
        BigDecimal totalRevenue = BigDecimal.ZERO;
        if(numberOfTicketsSold == 1) {
            totalRevenue = new BigDecimal(30);
        }
        return totalRevenue;
    }

}

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

测试驱动开发之初窥门径_TDD_02


到这里,第二个步骤”绿色“就完成了。

接下来我们开始重构TicketRevenue类的代码。

package com.lovo;

import java.math.BigDecimal;

public class TicketRevenue {
    private final static int 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;

public class TicketRevenueTest {
    private TicketRevenue ticketRevenue;
    private BigDecimal expectedRevenue;

    @Before
    public void setUp() {
        ticketRevenue = new TicketRevenue();
    }

    @Test(expected = IllegalArgumentException.class)
    public void failIfLessThanZeroTicketsAreSold() {
        ticketRevenue.estimateTotalRevenue(-1);
    }

    @Test
    public void zeroSalesEqualsZeroRevenue() {
        Assert.assertEquals(BigDecimal.ZERO, ticketRevenue.estimateTotalRevenue(0));
    }

    @Test
    public void oneTicketSoldIsThirtyInRevenue() {
        expectedRevenue = new BigDecimal("30");
        Assert.assertEquals(expectedRevenue, ticketRevenue.estimateTotalRevenue(1));
    }

    @Test
    public void tenTicketsSoldIsThreeHundredInRevenue() {
        expectedRevenue = new BigDecimal("300");
        Assert.assertEquals(expectedRevenue, ticketRevenue.estimateTotalRevenue(10));
    }

    @Test(expected = IllegalArgumentException.class)
    public void failIfMoreThanOneHundredTicketsAreSold() {
        ticketRevenue.estimateTotalRevenue(101);
    }
}

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

package com.lovo;

import java.math.BigDecimal;

public class TicketRevenue {
    private final static int 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;
    }

}

再次运行刚才的测试代码,检查一下你的bar是不是绿色的(JUnit的名言是:“Keep your bar green”)。当然,对于有代码洁癖的人来说,上述代码仍然稍显臃肿,没关系,再来一次重构吧。

package com.lovo;

import java.math.BigDecimal;

public class TicketRevenue {
    private final static int TICKET_PRICE = 30;

    public BigDecimal estimateTotalRevenue(int numberOfTicketsSold) 
            throws IllegalArgumentException {
        if(numberOfTicketsSold < 0 || numberOfTicketsSold > 100) {
            throw new IllegalArgumentException("门票销售数量必须在0到100之间");
        }

        return  new BigDecimal(TICKET_PRICE * numberOfTicketsSold);
    }

}

当你完成对代码的修改后,永远都不要忘记再来一次刚才的测试,仍然需要Keep your bar green。
如果我们使用面向对象的编程范式,那么对代码的重构应当遵循面向对象的设计原则。大神Robert Matin将这些原则总结为SOLID原则。

原则

英文

描述

单一职责原则(S)

Single Responsibility Principle

每个对象只做自己该做的事情

开闭原则(O)

Open-Closed Principle

接受扩展但不接受修改

里氏替换原则(L)

Liskov Substitution Principle

可以用子类型替换父类型

接口隔离原则(I)

Interface Segregation Principle

接口要小而专

依赖倒转原则(D)

Dependency Inversion Principle

依赖接口而不依赖实现

说明:上面的例子来自The Well-Grounded Java Developer一书(中文名《Java程序员修炼之道》),这本书覆盖了Java开发中很多实用的技术以及Java新的语言特性,有兴趣的可以阅读此书,相信你会从中得到很多收获。


标签:BigDecimal,门径,测试,TicketRevenue,import,estimateTotalRevenue,public,之初
From: https://blog.51cto.com/u_16166070/6522235

相关文章

  • 威纶触摸屏485直接控制监控台达vfd-m变频器程序 目前已测试成功台
    威纶触摸屏485直接控制监控台达vfd-m变频器程序目前已测试成功台达,三菱,施耐德变频器。功能很强大,道理也不复杂,只需两条通信线,可以实现在触摸屏或者电脑通过EB8000在线模拟模式下加usb转485转换头控制变频器正反转,停止,及频率设定,加减速,变频器加减速时间,频率上下限亦可设定,还能实现......
  • 【实用软件测试教程】6-功能测试
    文章目录6功能测试6.1系统测试概论6.2功能测试概述6.3功能测试的策略6.4功能测试的内容6.5功能测试的方法6.6.QuickTestProfessional(QTP)6功能测试功能模块是系统测试阶段的重点内容,软件系统开发的首要目标是确保功能正确。功能测试主要是根据软件系统的特征、操作描述和......
  • LabVIEW开发的测试设备软件代码和PLC程序 前
    LabVIEW开发的测试设备软件代码和PLC程序前几年给一台检测设备做的上位机软件,三条测试支路共用同一个状态机vi,每个支路可独立运行,按编号区分每路的控制,下位机为西门子200smart,上下位机通过ModBUSTCP/IP通信。可以给LabVIEW学习者带来一些开发思路和启发。LabVIEW开发的测......
  • Web自动化测试中的最佳实践和常见陷阱
    在现代软件开发中,Web自动化测试已经成为保证软件质量和提高开发效率的重要手段之一。然而,仅凭自动化测试工具和技术并不足以确保成功。下面我们将介绍一些Web自动化测试的最佳实践和常见陷阱,帮助您避免一些常见的错误和困难。首先,让我们来看一些Web自动化测试的最佳实践。首要问题......
  • 软件测试的冒烟测试
    软件测试是软件开发过程中的一个非常重要的部分,能够有效地保证软件的质量和用户体验。而在软件测试中,冒烟测试被认为是一项非常关键的测试工作,因为它可以帮助团队快速定位软件中可能存在的问题,并及时进行修复。什么是冒烟测试?冒烟测试(SmokeTesting)也叫做构建验证测试(BuildVerific......
  • 软件测试四大测试
    单元测试是一种针对程序中最小可测试单元(通常是函数或方法)的测试方法。下面是进行单元测试的一般步骤:1.确定被测试的单元:选择要测试的函数或方法,并分析其输入、输出、边界条件等特性。2.编写测试用例:根据被测试单元的特性,编写测试用例,包括针对不同情况的测试输入和期望......
  • 三菱FX3U-485ADP-MB通讯三种变频器程序 已实现测试的变频器:施耐德ATV312,
    三菱FX3U-485ADP-MB通讯三种变频器程序已实现测试的变频器:施耐德ATV312,三菱E700,台达VFD-M三款变频器,支持rtu的协议的变频器都可实现。需要硬件:FX3UPLC,FX3U-485ADP-MB通信扩展模块,施耐德ATV312变频器或台达vfd-m变频器或三菱E700变频器,fx3u-cnv-bd。通过modbusrtu通讯方式,可......
  • 如何实施可扩展性测试?
    可扩展性测试旨在验证系统或应用程序在面对增大的负载和用户数量时,能够保持性能、吞吐量和响应时间的稳定性和可伸缩性。以下是进行可扩展性测试的一般步骤:定义测试目标和指标:明确要测试的系统或应用程序的可扩展性需求,并定义相应的性能指标,如响应时间、吞吐量和并发用户数等。性能......
  • 如何实施可扩展性测试?
    可扩展性测试旨在验证系统或应用程序在面对增大的负载和用户数量时,能够保持性能、吞吐量和响应时间的稳定性和可伸缩性。以下是进行可扩展性测试的一般步骤:定义测试目标和指标:明确要测试的系统或应用程序的可扩展性需求,并定义相应的性能指标,如响应时间、吞吐量和并发用户数等。......
  • 用户测试:确保产品质量的关键一环
    用户测试:确保产品质量的关键一环在当今竞争激烈的市场中,产品的质量是企业脱颖而出的关键因素之一。为了确保产品的质量,用户测试成为了开发过程中不可或缺的一环。用户测试是通过让真实用户使用产品并提供反馈意见来验证产品的功能、易用性和用户体验。首先,用户测试能够......