Junit系列一 基本概述

JUnit是一个开源的java自动化单元测试框架。由 Erich Gamma 和 Kent Beck 与1997年开发完成。 Erich Gamma 是 GOF的作者 之一;Kent Beck 则在 XP 中有重要的贡献。Junit常常被开发者用来做单元测试的自动化测试框架。但是按照测试人员的角度来说,Junit更严格来讲是一个测试驱动器,我们不仅仅可以用其来做单元测试,也是可以以其为核心来做集成测试、系统测试。很多人分析Junit喜欢从开发角度从设计模式来分析,但是对于测试人员来说需要的是从中学习自动化测试框架的基本组成部分与结构模式。学习Junit不仅仅是学习其用法,更要从中学习一个自动化测试框架的有哪些组成部分?各个组成部分之间是如何协调运作来形成一个可扩展稳健的自动化测试框架。Junit可谓“麻雀虽小,五脏俱全。”,其包含一个自动化测试框架所有的要素部分,很多的测试框架,千变万化其实都离不开一个测试框架必有的组件与模式。本系列尝试从一个手工测用例转化为自动化执行以后的模式,来总结出一个自动化测试所必须的一个组成部分,并借用Junit来说明Junit中是如何实现这几个部分的。

基本概述

让我们从平时做测试的一个简单情形开始。想想平时我们是如何做测试的,或者说是如何执行测试过程的?是不是有用例?我们编写了用例,在用例里面说明了测试预置环境,测试输出数据,执行步骤,和预期的结果。例如:

拿到这个用例,是不是要一步步按照用例的说明来操作。在人工测试的情况下类似如下的情况,

在这个过程当中,测试人员是执行者,读取测试用例内容,操作被测对象,并判断结果是否正确。

那么,如果要把上述的过程自动化需要如何做呢?在自动化的过程中,人不在了,那么谁来读取测试用例呢?准确点说由谁来读取测试用例的文件,并逐条执行里面的用例呢?首先想到就是写一个程序,此程序能读取文件,并识别里面的逐条的测试用例,然后执行。这样的一个程序叫做测试执行器(Test Runner)。

有了执行器以后,又面临一个问题,在人工操过程中,人是可以识别文件里的文本用例,读懂它的步骤然后操作,但执行器就不行了,它是程序他无法识别文本的意思。程序识别程序,那么自然而然想到的方法就是把测试用例方法化,即把测试用例用程序的方法来表示,即测试方法(Test Method),因此执行器执行用例,就转化为执行器调用方法的过程。

我们进一步观察,测试方法,实时上,一个方法()Test Method 就代表一个测试用例,一个测试用例主要包含的重要信息,就是用例的执行环境是什么,执行步骤,预期值是什么,等等。类似的测试方法也需要包含四个步骤,称为测试四步骤(Four-Phase Test)

  • 环境设置
  • SUT调用
  • 结果验证
  • 环境清理

回顾之前所述,在测当中一个重要的步骤就是验证,手动测试中,肉眼查看运行结果并和预期对比来得出测试是否通过的结果。在自动化过程中自然需要这个过程,让测试方法自动验证测试结果呢?这部自动化测试中称为断言(Assert)。

在整个的测试过程当中还包过如何过滤测试方法、、测试数据管理、最终测试结果报告等等。

如上述,我们总结一下,在自动化测试当中需要涉及的部分包括:

  • 测试执行器(Runner):读取测试用例、执行测试用例、测试报告输出
  • 测试输入数据管理:如果测试需要外部输入数据,这些数据如何组织管理
  • 环境设置(setUp tearDown):主要设置用例执行前的环境、测试结束后清理测试环境;
  • 结果断言(Assert):如何来判断测试结果是否符合预期
  • 测试组织与流程控制:如何来组织测试用例,测试用例的过程如何控制

那么作为一个自动化的测试框架,Junit如何对上述各个组成部分进行处理呢,我们将从一个Junti入门例子开始,先对Junit做大致介绍,然后逐一演示Junit是如何实现上述各个部分的?只要大家理解了Junit对上述几个部分是怎么做的,后续学习其他测试驱动器就可以快速入手,比如TestNG-在讲完Junit后,我们会针对TestNG如何实现上是问题做简单说明,只要大家牢固掌握Junit实现上述问题的本质,学习testNG也就是分分钟的事情。

在Eclipse中使用JUnit4

在开始之前我们先把Junit引入到我们的Eclipse的项目中来,在Eclipse项目右键选中,如下图所示

在弹出的属性窗口中,首先在左边选择“Java Build Path”,然后到右上选择“Libraries”标签,之后在最右边点击“Add Library…”按钮,如下图所示:

然后在新弹出的对话框中选择JUnit4并点击确定,如上图所示,JUnit4软件包就被包含进我们这个项目了。

Junit 入门例子

假设我们有一个计算器类,包含两个方法,add和multiply。

public class Calculator {
    
    public int add(int one, int another) {
        return one + another;
    }

    public int multiply(int one, int another) {
        return one * another;
    }
}

对这两个方法,我们写两条happy path的测试用例:

要把这两条测试用例自动化执行,要涉及前面所述的几个步骤,首先是要测试用例脚本化,把测试用例的步骤用代码方法来表示,我们建一个类叫CalculatorTest,包含这两条测试用例:

public class CalculatorTest {
    
    public void testAdd(){
        
        Calculator calculator = new Calculator();
        int sum = calculator.add(6,7);
        if(sum == 13) {
            System.out.println("add() SUCCESS!");
        } else {
            System.out.println("add() FAIL!");
        }
    }
    
    public void testMultiply(){
        
        Calculator calculator = new Calculator();
        int product = calculator.multiply(8,9);
        if (product == 56) {
            System.out.println("multiply() SUCCESS!");
        } else {
            System.out.println("multiply() FAILs!");
        }
    }   
}

测试方法是不是要执行器来执行?如果我没有Junit这样的框架,我们怎么执行上述的测试方法呢?如下也许是一种方法:

public class Client {
    public static void main(String[] args) {
        CalculatorTest test = new CalculatorTest();
        test.testAdd();
        test.testMultiply();
    }
}

然后再通过某种方式运行这个main 方法,查看打印的输出,来验证测试是成功还是失败。想想一下,如果我们有很多的类,每个类都有很多方法,每个方法都要写很多的分支来判断结果,而且还要写个main方法一个个去调用各个类方法,明显是不可行呢?如果有一个程序--执行器,能够自动创建一个测试类,并自动调用里面的各个测试方法那么就简化很多了。Junit框架就提供了这样的功能,Junit内置的执行器,能够读取一个类,并执行里面的测试方法,要让执行者自动找到测试方法并调用,必须要有一定的约定,比如告诉执行器说用test开头的方法就是测试方法(Junit 3所采用的方法)或者给测试方法某个标注来表示测试方法,那么执行器就会按着这个约定去调用类里的有特定命名方式或者有特定标志的测试方法。我们来Junit来改写上述的测试:

public class CalculatorTest {
    
    @Test
    public void testAdd(){
        
        Calculator calculator = new Calculator();
        int sum = calculator.add(6,7);
        if(sum == 13) {
            System.out.println("add() SUCCESS!");
        } else {
            System.out.println("add() FAIL!");
        }
    }
    
    @Test
    public void testMultiply(){
        
        Calculator calculator = new Calculator();
        int product = calculator.multiply(8,9);
        if (product == 56) {
            System.out.println("multiply() SUCCESS!");
        } else {
            System.out.println("multiply() FAILs!");
        }
    }       
}

大家注意到每个测试方多了一个注解@Test,这个注解的作用的就是告诉Junit的Test Runner 这是一个测试方法,Test Runner就会调用这个方法。这时候你就不用自己写一个main来执行这些方法,在Eclipse下右键 Run as Junit Test,Junit的测试执行器就会自动运行里面的测试方法。

还有两个地方:一个是设置部分,每个测试方法都创建了一个Calculator,这个重复的代码是否可以抽取出来呢?还有判断结果部分分支复杂,能否简化了?Junit提供fixture和断言Assert相关方案来解决这两个问题,看再一次用Junit简化后的测试代码:

public class CalculatorTest {
    
    // fixture部分
    public Calculator calculator;
    
    @Before
    public void setUp(){
        calculator = new Calculator();
    }
    
    //测试方法
    @Test
    public void testAdd(){
        
        calculator = new Calculator();
        int sum = calculator.add(6,7);
        //断言部分
        Assert.assertEquals(13, sum);
    }
    
    @Test
    public void testMultiply(){
        
        calculator = new Calculator();
        int product = calculator.multiply(8,9);
        //断言部分
        Assert.assertEquals(72, product);   
    }       

}

上述@Berfore 表示此方法在每个测试方法运行之前运行一次。Assert.assertEquals判断实际结果与预期是否一致。至此,这就是一个简单完整的Junit测试代码。用Junit执行器运行后有如下图的测试结果展示:

上述的代码中有疑问的地方先不管,这只是让大家体会一些一个Junit测试的整体流程,各个注解后面会有详细介绍。这边讲一些测试的统一结构和命名问题。

统一的测试结构

回顾我们的第一个Junit测试,可以总结出一个良好的测试方法包含四个步骤,就是上回我们提到的:

  • 设置:称为Fixture
  • 执行:掉测试方法
  • 断言:判断结果是否预期
  • 清理:清理测试,相关例子后续会看到

极力建议再写测试过程中中应该在每个阶段开始时做好注释。

测试方法命名规则

Junit4之前,注解,Junit必须采用一种方法来区分普通的方法和测试方法,当时采用的办法标识 以test为前缀的方法名、public、非static、void、无参的方法为测试方法,因此,测试方法名称都testFoo,testBar诸如此类。

但是Junit设计师认为这种的命名方式有时候无法真实揭示测试方法真实的用意,当一个方法对应的测试方法多了以后,甚至还会造成一些混乱,因此在Junit采用@Test注解来标识测试方法后,Junit取消测试方法名称必须test开头的规定,而是可以随意命名,但是大部分人还是以test开头。

后面有人提出 行为表达式(Behavior-expressing patterns)的命名方式:

[UnitOfWork_StateUnderTest]

下划线可以用with、if 等此替换。这种方式的作用是希望能让测试用例和代码文档一样易于阅读,比如上述的代码写久了,回头去看不知道方法testMultiply在乘积溢出情况下会返回什么,如果你的测试名称是这么写的:

multiplyOverflowWithException()

那么看测试方法的人就知道这测试方法,在溢出时会抛异常

集体怎么命名取决与你们的团队是如何约定,看个人喜好。

总结

我们讨论手工测试自动化后,需要处理的几个步骤,包括测试方法的读取与执行、测试执行环境的设置、测试结果的判断、测试报告、测试数据的管理、测试用例的组织等方面,一般的自动化测试框架要解决也就这几个方面,对于Junit testNg无不如此。对于Junit 我们将从测试执行、断言、执行器、异常测试、测试组织等几个大方面详细介绍它的使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 148,637评论 1 318
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 63,443评论 1 266
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 99,164评论 0 218
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,075评论 0 188
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,080评论 1 266
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,365评论 1 184
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,901评论 2 283
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,649评论 0 176
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,122评论 0 223
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,734评论 2 225
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,093评论 1 236
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,548评论 2 222
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,028评论 3 216
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,765评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,291评论 0 178
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,162评论 2 239
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,293评论 2 242

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,036评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,145评论 6 345
  • 1.测试与软件模型 软件开发生命周期模型指的是软件开发全过程、活动和任务的结构性框架。软件项目的开发包括:需求、设...
    宇文臭臭阅读 6,654评论 5 100
  • 1.测试与软件模型 软件开发生命周期模型指的是软件开发全过程、活动和任务的结构性框架。软件项目的开发包括:需求、设...
    Mr希灵阅读 21,815评论 7 277
  • 简介 测试 在软件开发中是一个很重要的方面,良好的测试可以在很大程度决定一个应用的命运。软件测试中,主要有3大种类...
    Whyn阅读 5,641评论 0 2