首页 > 其他分享 >单元测试之道junit

单元测试之道junit

时间:2022-10-14 22:44:06浏览次数:86  
标签:... 转换 代码 单元测试 public 之道 测试 junit

一、分享前提问,一个复杂的功能怎么可以保证高效和质量?

 

A需求例如:

我们考虑出租车 (Taxi) 计价 (Calculate) 问题:

l 不大于 2 公里时只收起步价 6 元

l 超过 2 公里时每公里 0.8 元

l 超过 8 公里则每公里加收 50% 长途费

l 停车等待时加收每分钟 0.25 元

l 最后计价的时候司机 (Driver) 会四舍五入只收 (Charge) 到元

请写一个程序计算司机最终收费的数额。

 

 

B需求例如:每天监测各个机构的异常指标,假如异常就发送邮件及异常指标数据给对应机构的负责人。

  1. A机构,指标1:大于1个不同交费帐户退费至同一帐户数,指标2投保人变更后做保全退费的保单,指标N...
  2. B机构,指标1:投资尽调报告关键信息不完整的母基金项目,指标2:不存在立项建议书的直投项目,指标N...
  3. 机构N...

 

     B需求优化1:增加发送短信到对应机构的负责人

   B需求优化2:接收者可以配置指定接收人。

B需求优化3:把指标分成异常,正常,警告三种分别发送给负责人。

 


二、为什么需要单元测试

优势

  1. 提升代码质量
  2. 减少线上bug
  3. 提升项目上线成功率(因开发时已分析和测试大部分逻辑,后期进度可控)
  4. 增加代码可维护性(未来扩展,优化性能,找bug时)
  5. 最重要是减少加班时间

劣势

增加代码工作量,至少1比1~3的代码量

 

 

引用书的内容

 

 

 

 

 

 

三、使用junit编写单元测试

  1. 编写测试代码的步骤

l 准备测试的所需要的各种条件(创建所有必须的对象,分配必要的资源等等)

l 调用要测试的方法

l 验证被测试的方法的行为和期望值是否一致

l 完成后清理各种资源

  1. 认识junit

l TestCase:字面意思,测试用例。为一个或多个方法提供测试方法,一般是一个类对应一个case(case里面包含多个方法的测试)。
TestSuite:测试集合,即一组测试。一个test suite是把多个相关测试归入一组的快捷方式。如果自己没有定义,Junit会自动提供一个test suite ,包括TestCase中的所有测试。
TestRunner:测试运行器。执行test suite的程序。

l TestResult:集合了执行测试样例的所有结果

l 断言Assert:void assertEquals​(boolean expected, boolean actual)​
检查两个变量或者等式是否平衡;​void assertFalse​(boolean condition)​;void assertNotNull​(Object object)​
检查对象不是空的;也可以验证异常情况assertTrue(e instanceof NumberFormatException);

l 运行流程:@BeforeClass;@AfterClass;@Before;@After

public class JunitFlowTest {
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        System.out.println("beforeClass...");
    }
    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        System.out.println("afterClass...");
    }
    @Before
    public void setUp() throws Exception {
        System.out.println("before...");
    }
    @After
    public void tearDown() throws Exception {
        System.out.println("after");
    }
    @Test
    public void test1() {
        System.out.println("test1方法...");
    }
    @Test
    public void test2(){
        System.out.println("test2方法...");
    }}

 

在上面的代码中,我们使用了两个测试方法,还有junit运行整个流程方法。我们可以运行一下,就会出现下面的运行结果:

beforeClass...
before...
test1方法...
afterbefore...
test2方法...
afterafterClass...

从上面的结果我们来画一张流程图就知道了:

 

 

 

  1. 案例

这里我们要测试的功能超级简单,就是加减乘除法的验证。

public class Calculate {
    public int add(int a,int b) {
        return a + b;
    }
    public int subtract(int a, int b) {
        return a - b;
    }
    public int multiply(int a,int b) {
        return a * b;
    }
    public int divide(int a ,int b) {
        return a / b;
    }}

然后我们看看如何使用junit去测试。

public class CalculateTest {
    @Test
    public void testAdd() {
        assertEquals(2, new Calculate().add(1,1));
    }
    @Test
    public void testSubtract() {
        assertEquals(8, new Calculate().subtract(10,2));
    }
    @Test
    public void testMultiply() {
        assertEquals(6, new Calculate().multiply(3, 2));
    }
    @Test
    public void testDivide() {
        assertEquals(5, new Calculate().divide(10, 2));
    }}

使用测试套件,把这些测试类嵌套在一起。

@RunWith(Suite.class)@Suite.SuiteClasses({CalculateTest.class,Test1.class,Test2.class等相关测试类})public class SuiteTest {
    /*
     * 写一个空类:不包含任何方法
     * 更改测试运行器Suite.class
     * 将测试类作为数组传入到Suite.SuiteClasses({})中
     */
}

 

四、好的单元测试所具有的品质

基本的单元测试要求Right-BICEP

l Right-结果是否正确?

l B-是否所有边界条件都是正确?(null,数字极值,正负数,非正常输入等)

l I-能查一下反向关联吗?(例如插入数据库,使用查询该记录)

l C-能用其他手段交叉检查结果?(算法,能否用其他算法验证)

l E-是否检查错误条件?(读取文件,请求其他系统的接口,有没有考虑异常)

l P-是否满足性能要求?

好的单元测试所具有的品质

l 自动化(调用测试的自动化,检查结果的自动化)

l 彻底化(测试覆盖率高,如代码的每个分支,可能抛出的异常,极端数据等)

l 可重复

l 独立的

l 专业的(使用设计模式,提高测试效率,例如公共的逻辑可以抽出抽象的类,让子类实现具体不同的业务,在多重逻辑判断中,使用过滤器设计模式,等)

l 代码可测试性高(当一个方法无法简单写出对应单元测试,即代码需要重构拆分逻辑,方法的逻辑尽量简单,一个方法只做一个事情)

l 测试与评审(让组员同事互相评审,或者交叉写单元测试)

五、在项目中进行单元测试

l Mock对象(当方法需要请求其他系统时,使用Mock模拟其他系统的接口的结果)

   网络搜索Mockito的使用方法

六、面向测试的设计

对应上文的代码可测试性,与下文的TDD,其实这个实现起来牵涉内容很多,简单说,最好是易于编写单元测试,方法模块逻辑解耦等,通俗说就是一个方法只做一个功能,这样就能写出对应的单元测试。

例如:编写了一个周期执行任务的功能,那么可以或者执行时间的值,来判断是否正常,而不是等待任务执行。

例如:一个方法逻辑比较复杂,总行数越写越长,需要思考是否有逻辑重复,不同业务逻辑是否解耦。

举例:某系统的批量文件转换pdf任务。

原代码:

public String batchWord2Pdf(String orgCode) {
    // 获取各个机构公司的文件信息及转换
    if (orgCode.equals(Constant.ORG.orgA)) {

//获取A文件及转换
        orgAWord2Pdf();
    }
    if (orgCode.equals(Constant.ORG.orgB)) {

//获取B文件及转换
        orgBWord2Pdf();
    }
    if (orgCode.equals(Constant.ORG.orgC)) {

//获取C文件及转换
        orgCWord2Pdf();
    }
    return "Y";
}

新增一个机构就需要增加较多的代码,见项目代码

com.xxx.service.impl.FileConvertServiceImpl(一个大类把所有功能都写在一起,已经600多行了,代码行数太多也是坏味道)

 

 

 

可见业务代码和转换逻辑代码耦合,且业务代码中存在重复逻辑的代码,这在单元测试和业务测试产生重复的工作量,如果继续新增机构N估计超过千行,而且假如逻辑有bug需要修复,修改代码行数N倍,增加需求代码也是N倍,测试也是N倍。

合理的代码设计,应该是把转换流程,记录转换结果标识到数据库的流程抽象,取各个机构的具体文件数据在子类。

 

 

 

处理过程抽象类

 

 

 

获取orgA文件类

 

 

 

获取orgB文件类

 

 

 

转换PDF类

 

 

 

同时降低单元测试的数量,再新增机构也不会成倍的增加测试工作量。

原代码

 

重构后

机构

测试内容

机构

测试内容

orgA

查询文件,判断是否转换,转换,保存转换记录(或记录异常)

orgA

查询文件

orgB

查询文件,判断是否转换,转换,保存转换记录(或记录异常)

orgB

查询文件

orgN

查询文件,判断是否转换,转换,保存转换记录(或记录异常)

orgN

查询文件

 

 

转换流程(通用方法)

判断是否转换,转换,保存转换记录(或记录异常)

 

 

文件转换PDF(通用方法)

请求FileConvert服务转换

举例:spring的filter设计

相信大家都可能用过spring的filter来实现登录拦截,流程如下

 

 

 

filter的责任链模式设计可以为一个Web应用组件部署多个过滤器,这些过滤器组成一个过滤器链,每个过滤器只执行某个特定的操作或者检查。

 

 

 

具体代码请看spring源码及设计模式的责任链模式。大家在项目实践中可以来处理某些数据是否满足哪些条件规则,这样在开发不同规则都是独立可测的,可以放心的新增或者修改规则。

 

 

案例:阿里巴巴的RocketMQ的储存消息默认实现DefaultMessageStore

 

 

 

类的方法

 

 

 

对应的单元测试

 

 

 

 

 

 

 

七、测试驱动开发TDD与结对编程

1先实现TDD测试驱动开发

大概流程:
1.1.编写一个失败的单元测试。
1.2.修改产品代码使之通过单元测试。
1.3.重构单元测试和产品代码。

(在重复这个过程中会发现代码是否方便测试,逻辑是否复杂,逻辑是否解耦,是否可测,及可以重构)

2 找一个同事伙伴基友,你写代码,ta写单元测试

 

 

 

参考资料

书籍

《单元测试之道-使用JUnit(Java版)].Andrew.Hunt》

《重构 改善既有代码的设计》

 《设计模式》

视频资料:thoughtworks的员工分享

TDD理论基础_哔哩哔哩_bilibili

TDD实战@计算斐波那契数_哔哩哔哩_bilibili

标签:...,转换,代码,单元测试,public,之道,测试,junit
From: https://www.cnblogs.com/ihuotui/p/16793249.html

相关文章

  • 架构整洁之道
    目标用最少的人力成本满足构建和维护该系统的需求衡量指标版本迭代--工程师团队规模版本迭代--代码总行数版本迭代--代码变更行数软件系统的价值行为价值按需求文......
  • 单元测试之Mockito+Junit使用和总结
    https://www.letianbiji.com/java-mockito/mockito-thenreturn.htmlMockito使用thenReturn设置方法的返回值thenReturn用来指定特定函数和参数调用的返回值。比如......
  • springboot~对mybatis的start包进行单元测试
    一个start包,它不需要有springboot启动类,它只提供一切公用的功能,被其它包依赖就行了,通过META-INF/spring.factories或者META-INF/spring/org.springframework.boot.autoconf......
  • 程序员修炼之道:从小工到专家阅读笔记3
    注重实效的途径2.1重复的危害   1.DRY:系统中的每一项知识都是必须具有单一、无歧义、权威的表示   DRY:Donotrepeatyourself   2.文档与代码:你撰写文......
  • 程序员修炼之道: 从小工到专家笔记4
    3.基本工具花时间学习使用这些工具,有一天你将会惊奇地发现,你的手指在键盘上移动,操纵文本,却不用进行有意识的思考。工具将变成你的双手的延伸。3.5调试   1.你需要关......
  • 【测试】Selenium录制单元测试脚本
    因业务性质发生变化,公司有要求做业务测试自动化用以替代人工进行可重复执行操作。基于学习曲线比较平缓且容易上手考虑,最终选择了Selenium作为自动化测试框架。安装Selenium......
  • 《程序员修炼之道:从小工到专家》
    在这本书里面,作者通过一些很形象的比喻,解释了一些项目开发中的重要内容:首先石头汤和青蛙汤的故事,分别阐释了:简单原型到复杂系统的蜕变,还有就是自己身处环境的变化(如果你长......
  • junit参数化测试的使用方法
     JUnit参数化测试的五个步骤:(1)为准备使用参数化测试的测试类指定特殊的运行器org.junit.runners.Parameterized。(2)为测试类声明几个变量,分别用于存放期望值和测试所用......
  • Lombok 使用在 IDEA 中进行 JUnit 测试的时候提示 variable log 错误
    提示的错误信息如下:D:\WorkDir\USRealEstate\Source-Code\Common\common\src\test\java\com\ossez\common\tests\ConfTest.java:28:9java:cannotfindsymbolsymbol:v......
  • Linux黑客的python编程之道 pdf
    高清扫描版下载链接:https://pan.baidu.com/s/1r6u7cO734ZD4CVHq7EaFsA点击这里获取提取码 ......