阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Java 单元测试的 7 个技巧

355次阅读
没有评论

共计 5653 个字符,预计需要花费 15 分钟才能阅读完成。

导读 测试是开发的一个非常重要的方面,可以在很大程度上决定一个应用程序的命运。良好的测试可以在早期捕获导致应用程序崩溃的问题,但较差的测试往往总是导致故障和停机。

虽然有三种主要类型的软件测试:单元测试,功能测试和集成测试,但是在这篇博文中,我们将讨论开发人员级单元测试。在我深入讲述具体细节之前,让我们先来回顾一下这三种测试的详细内容。

软件开发测试的类型

单元测试用于测试各个代码组件,并确保代码按照预期的方式工作。单元测试由开发人员编写和执行。大多数情况下,使用 JUnit 或 TestNG 之类的测试框架。测试用例通常是在方法级别写入并通过自动化执行。

集成测试检查系统是否作为一个整体而工作。集成测试也由开发人员完成,但不是测试单个组件,而是旨在跨组件测试。系统由许多单独的组件组成,如代码,数据库,Web 服务器等。集成测试能够发现如组件布线,网络访问,数据库问题等问题。

功能测试通过将给定输入的结果与规范进行比较来检查每个功能是否正确实现。通常,这不是在开发人员级别的。功能测试由单独的测试团队执行。测试用例基于规范编写,并且实际结果与预期结果进行比较。有若干工具可用于自动化的功能测试,如 Selenium 和 QTP。

如前所述,单元测试可帮助开发人员确定代码是否正常工作。在这篇博文中,我将提供在 Java 中单元测试的有用提示。

1. 使用框架来用于单元测试

Java 提供了若干用于单元测试的框架。TestNG 和 JUnit 是最流行的测试框架。JUnit 和 TestNG 的一些重要功能:

  • 易于设置和运行。
  • 支持注释。
  • 允许忽略或分组并一起执行某些测试。
  • 支持参数化测试,即通过在运行时指定不同的值来运行单元测试。
  • 通过与构建工具,如 Ant,Maven 和 Gradle 集成来支持自动化的测试执行。

EasyMock 是一个模拟框架,是单元测试框架,如 JUnit 和 TestNG 的补充。EasyMock 本身不是一个完整的框架。它只是添加了创建模拟对象以便于测试的能力。例如,我们想要测试的一个方法可以调用从数据库获取数据的 DAO 类。在这种情况下,EasyMock 可用于创建返回硬编码数据的 MockDAO。这使我们能够轻松地测试我们意向的方法,而不必担心数据库访问。

2. 谨慎使用测试驱动开发!

测试驱动开发(TDD)是一个软件开发过程,在这过程中,在开始任何编码之前,我们基于需求来编写测试。由于还没有编码,测试最初会失败。然后写入最小量的代码以通过测试。然后重构代码,直到被优化。

目标是编写覆盖所有需求的测试,而不是一开始就写代码,却可能甚至都不能满足需求。TDD 是伟大的,因为它导致简单的模块化代码,且易于维护。总体开发速度加快,容易发现缺陷。此外,单元测试被创建作为 TDD 方法的副产品。

然而,TDD 可能不适合所有的情况。在设计复杂的项目中,专注于最简单的设计以便于通过测试用例,而不提前思考可能会导致巨大的代码更改。此外,TDD 方法难以用于与遗留系统,GUI 应用程序或与数据库一起工作的应用程序交互的系统。另外,测试需要随着代码的改变而更新。

因此,在决定采用 TDD 方法之前,应考虑上述因素,并应根据项目的性质采取措施。

3. 测量代码覆盖率

代码覆盖率衡量(以百分比表示)了在运行单元测试时执行的代码量。通常,高覆盖率的代码包含未检测到的错误的几率要低,因为其更多的源代码在测试过程中被执行。测量代码覆盖率的一些最佳做法包括:

  • 使用代码覆盖工具,如 Clover,Corbetura,JaCoCo 或 Sonar。使用工具可以提高测试质量,因为这些工具可以指出未经测试的代码区域,让你能够开发开发额外的测试来覆盖这些领域。
  • 每当写入新功能时,立即写新的测试覆盖。
  • 确保有测试用例覆盖代码的所有分支,即 if / else 语句。

高代码覆盖不能保证测试是完美的,所以要小心!

下面的 concat 方法接受布尔值作为输入,并且仅当布尔值为 true 时附加传递两个字符串:

< class="hljs typescript">public String concat(boolean append, String a,String b) {String result = null;
        If (append) {result = a + b;}
        return result.toLowerCase();}

以下是上述方法的测试用例:

<class="hljs less">@Test
public void testStringUtil() {String result = stringUtil.concat(true, "Hello", "World");
     System.out.println("Result is"+result);
}

在这种情况下,执行测试的值为 true。当测试执行时,它将通过。当代码覆盖率工具运行时,它将显示 100% 的代码覆盖率,因为 concat 方法中的所有代码都被执行。但是,如果测试执行的值为 false,则将抛出 NullPointerException。所以 100% 的代码覆盖率并不真正表明测试覆盖了所有场景,也不能说明测试良好。

4. 尽可能将测试数据外部化

在 JUnit4 之前,测试用例要运行的数据必须硬编码到测试用例中。这导致了限制,为了使用不同的数据运行测试,测试用例代码必须修改。但是,JUnit4 以及 TestNG 支持外部化测试数据,以便可以针对不同的数据集运行测试用例,而无需更改源代码。

下面的 MathChecker 类有方法可以检查一个数字是否是奇数:

< class="hljs kotlin">public class MathChecker {public Boolean isOdd(int n) {if (n%2 != 0) {return true;
            } else {return false;
            }
        }
    }

以下是 MathChecker 类的 TestNG 测试用例:

< class="hljs less">public class MathCheckerTest {
        private MathChecker checker;
        @BeforeMethod
        public void beforeMethod() {checker = new MathChecker();
        }
        @Test
        @Parameters("num")
        public void isOdd(int num) {System.out.println("Running test for"+num);
          Boolean result = checker.isOdd(num);
          Assert.assertEquals(result, new Boolean(true));
        }
    }
TestNG

以下是 testng.xml(用于 TestNG 的配置文件),它具有要为其执行测试的数据:

< class="hljs xml"><?xml version="1.0" encoding="UTF-8"?>
    <suite name="ParameterExampleSuite" parallel="false">
    <test name="MathCheckerTest">
    <classes>
      <parameter name="num" value="3"></parameter>
      <class name="com.stormpath.demo.MathCheckerTest"/>
    </classes>
     </test>
     <test name="MathCheckerTest1">
    <classes>
      <parameter name="num" value="7"></parameter>
      <class name="com.stormpath.demo.MathCheckerTest"/>
    </classes>
     </test>
    </suite>

可以看出,在这种情况下,测试将执行两次,值 3 和 7 各一次。除了通过 XML 配置文件指定测试数据之外,还可以通过 DataProvider 注释在类中提供测试数据。

JUnit

与 TestNG 类似,测试数据也可以外部化用于 JUnit。以下是与上述相同 MathChecker 类的 JUnit 测试用例:

< class="hljs java">@RunWith(Parameterized.class)
    public class MathCheckerTest {private int inputNumber;
     private Boolean expected;
     private MathChecker mathChecker;
     @Before
     public void setup(){mathChecker = new MathChecker();}
        // Inject via constructor
        public MathCheckerTest(int inputNumber, Boolean expected) {this.inputNumber = inputNumber;
            this.expected = expected;
        }
        @Parameterized.Parameters
        public static Collection<Object[]> getTestData() {return Arrays.asList(new Object[][]{{1, true},
                    {2, false},
                    {3, true},
                    {4, false},
                    {5, true}
            });
        }
        @Test
        public void testisOdd() {System.out.println("Running test for:"+inputNumber);
            assertEquals(mathChecker.isOdd(inputNumber), expected);
        }
    }

可以看出,要对其执行测试的测试数据由 getTestData()方法指定。此方法可以轻松地修改为从外部文件读取数据,而不是硬编码数据。

5. 使用断言而不是 Print 语句

许多新手开发人员习惯于在每行代码之后编写 System.out.println 语句来验证代码是否正确执行。这种做法常常扩展到单元测试,从而导致测试代码变得杂乱。除了混乱,这需要开发人员手动干预去验证控制台上打印的输出,以检查测试是否成功运行。更好的方法是使用自动指示测试结果的断言。

下面的 StringUti 类是一个简单类,有一个连接两个输入字符串并返回结果的方法:

< class="hljs typescript">public class StringUtil {public String concat(String a,String b) {return a + b;
        }
    }

以下是上述方法的两个单元测试:

< class="hljs less">@Test
    public void testStringUtil_Bad() {String result = stringUtil.concat("Hello", "World");
         System.out.println("Result is"+result);
    }
    @Test
    public void testStringUtil_Good() {String result = stringUtil.concat("Hello", "World");
         assertEquals("Hello World", result);
    }

testStringUtil\_Bad 将始终传递,因为它没有断言。开发人员需要手动地在控制台验证测试的输出。如果方法返回错误的结果并且不需要开发人员干预,则 testStringUtil\_Good 将失败。

6. 构建具有确定性结果的测试

一些方法不具有确定性结果,即该方法的输出不是预先知道的,并且每一次都可以改变。例如,考虑以下代码,它有一个复杂的函数和一个计算执行复杂函数所需时间(以毫秒为单位)的方法:

< class="hljs java">public class DemoLogic {private void veryComplexFunction(){//This is a complex function that has a lot of database access and is time consuming
        //To demo this method, I am going to add a Thread.sleep for a random number of milliseconds
        try {int time = (int) (Math.random()*100);
            Thread.sleep(time);
        } catch (InterruptedException e) {// TODO Auto-generated catch block
            e.printStackTrace();}
    }
    public long calculateTime(){long time = 0;
        long before = System.currentTimeMillis();
        veryComplexFunction();
        long after = System.currentTimeMillis();
        time = after - before;
        return time;
    }
    }0

在这种情况下,每次执行 calculateTime 方法时,它将返回一个不同的值。为该方法编写测试用例不会有任何用处,因为该方法的输出是可变的。因此,测试方法将不能验证任何特定执行的输出。

7. 除了正面情景外,还要测试负面情景和边缘情况

通常,开发人员会花费大量的时间和精力编写测试用例,以确保应用程序按预期工作。然而,测试负面测试用例也很重要。负面测试用例指的是测试系统是否可以处理无效数据的测试用例。例如,考虑一个简单的函数,它能读取长度为 8 的字母数字值,由用户键入。除了字母数字值,应测试以下负面测试用例:

  • 用户指定非字母数字值,如特殊字符。
  • 用户指定空值。
  • 用户指定大于或小于 8 个字符的值。

类似地,边界测试用例测试系统是否适用于极端值。例如,如果用户希望输入从 1 到 100 的数字值,则 1 和 100 是边界值,对这些值进行测试系统是非常重要的。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-24发表,共计5653字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
7987263
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
开源MoneyPrinterTurbo 利用AI大模型,一键生成高清短视频!

开源MoneyPrinterTurbo 利用AI大模型,一键生成高清短视频!

  开源 MoneyPrinterTurbo 利用 AI 大模型,一键生成高清短视频! 在短视频内容...
零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face 免费服务器 +Docker 快速部署 HertzBeat 监控平台 ...
星哥带你玩飞牛NAS-1:安装飞牛NAS

星哥带你玩飞牛NAS-1:安装飞牛NAS

星哥带你玩飞牛 NAS-1:安装飞牛 NAS 前言 在家庭和小型工作室场景中,NAS(Network Atta...
安装并使用谷歌AI编程工具Antigravity(亲测有效)

安装并使用谷歌AI编程工具Antigravity(亲测有效)

  安装并使用谷歌 AI 编程工具 Antigravity(亲测有效) 引言 Antigravity...
每天一个好玩的网站-手机博物馆-CHAZ 3D Experience

每天一个好玩的网站-手机博物馆-CHAZ 3D Experience

每天一个好玩的网站 - 手机博物馆 -CHAZ 3D Experience 一句话介绍:一个用 3D 方式重温...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
手把手教你,购买云服务器并且安装宝塔面板

手把手教你,购买云服务器并且安装宝塔面板

手把手教你,购买云服务器并且安装宝塔面板 前言 大家好,我是星哥。星哥发现很多新手刚接触服务器时,都会被“选购...
星哥带你玩飞牛NAS-16:飞牛云NAS换桌面,fndesk图标管理神器上线!

星哥带你玩飞牛NAS-16:飞牛云NAS换桌面,fndesk图标管理神器上线!

  星哥带你玩飞牛 NAS-16:飞牛云 NAS 换桌面,fndesk 图标管理神器上线! 引言 哈...
还在找免费服务器?无广告免费主机,新手也能轻松上手!

还在找免费服务器?无广告免费主机,新手也能轻松上手!

还在找免费服务器?无广告免费主机,新手也能轻松上手! 前言 对于个人开发者、建站新手或是想搭建测试站点的从业者...
让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级

让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级

让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级 大家好,我是星哥,之前写了一篇文章 自己手撸一...
12.2K Star 爆火!开源免费的 FileConverter:右键一键搞定音视频 / 图片 / 文档转换,告别多工具切换

12.2K Star 爆火!开源免费的 FileConverter:右键一键搞定音视频 / 图片 / 文档转换,告别多工具切换

12.2K Star 爆火!开源免费的 FileConverter:右键一键搞定音视频 / 图片 / 文档转换...