Jasmine单元测试教程及示例

Jasmine是 popular 的JavaScript单元测试框架之一,能够测试同步和异步JavaScript代码。它用于BDD(行为驱动的开发)编程中,该编程更多地侧重于业务价值而不是技术细节。在本Jasmine教程中,我们将详细介绍Jasmine框架,从设置说明到了解测试用例的输出。

目录

1。 Jasmine设置配置
2.编写套件和规格
3.设置和拆卸
4。 Jasmine描述块
5。 Jasmine Matchers  
6.禁用套件和规格
7.使用Jasmine Spies 
8.最终想法

1.茉莉花安装程序配置

首先下载茉莉花框架,并将其解压缩到您的项目文件夹中。我建议在您的应用程序中可能已经存在的或/jasmine下创建一个单独的文件夹。/js/javascript

您将在分发捆绑包中获得以下四个文件夹/文件:

  1. /src :包含您要测试的JavaScript源文件
  2. /lib :包含框架文件
  3. /spec :包含JavaScript测试文件
  4. SpecRunner.html :是测试用例运行器HTML文件

您可以删除/src文件夹;并从文件中当前位置引用源SpecRunner.html文件。默认文件如下所示,您将需要更改/src/spec文件夹中包含的文件。

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Jasmine Spec Runner v2.4.1</title>
	
	<link rel="shortcut icon" type="image/png" href="lib/jasmine-2.4.1/jasmine_favicon.png">
	<link rel="stylesheet" href="lib/jasmine-2.4.1/jasmine.css">
	
	<script src="lib/jasmine-2.4.1/jasmine.js"></script>
	<script src="lib/jasmine-2.4.1/jasmine-html.js"></script>
	<script src="lib/jasmine-2.4.1/boot.js"></script>
	
	<!-- include source files here..。 -->
	<script src="src/Player.js"></script>
	<script src="src/Song.js"></script>
	
	<!-- include spec files here..。 -->
	<script src="spec/SpecHelper.js"></script>
	<script src="spec/PlayerSpec.js"></script>
</head>

<body></body>
</html>

在此演示中,我删除了/src文件夹,并将引用其当前位置的文件。当前的文件夹结构如下:

茉莉花文件夹结构
茉莉花文件夹结构

为了专注于Jasmine的功能,我将创建一个MathUtils.js具有一些基本操作的简单JS文件,我们将对这些功能进行单元测试。

MathUtils = function() {};

MathUtils.prototype.sum = function(number1, number2) {
		return number1 + number2;
}

MathUtils.prototype.substract = function(number1, number2) {
	return number1 - number2;
}

MathUtils.prototype.multiply = function(number1, number2) {
	return number1 * number2;
}

MathUtils.prototype.divide = function(number1, number2) {
	return number1 / number2;
}

MathUtils.prototype.average = function(number1, number2) {
	return (number1 + number2) / 2;
}

MathUtils.prototype.factorial = function(number) {
	if (number < 0) {
		throw new Error("There is no factorial for negative numbers");
	} else if (number == 1 || number == 0) {
		return 1;
	} else {
		return number * this.factorial(number - 1);
	}
}

并且在中添加文件引用后SpecRunner.html,文件内容将为:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Jasmine Spec Runner v2.4.1</title>
	
	<link rel="shortcut icon" type="image/png"
		href="lib/jasmine-2.4.1/jasmine_favicon.png">
	<link rel="stylesheet" href="lib/jasmine-2.4.1/jasmine.css">
	
	<script src="lib/jasmine-2.4.1/jasmine.js"></script>
	<script src="lib/jasmine-2.4.1/jasmine-html.js"></script>
	<script src="lib/jasmine-2.4.1/boot.js"></script>
	
	<!-- include source files here..。 -->
	<script src="../MathUtils.js"></script>
	
	<!-- include spec files here..。 -->
	<script src="spec/MathUtils.js"></script>
</head>

<body></body>
</html>

2.茉莉花套件和规格

在Jasmine中,有两个重要术语– suitespec

2.1。套房

Jasmine套件是一组测试用例,可用于测试JavaScript代码(JavaScript对象或函数)的特定行为。首先使用describe两个参数调用Jasmine全局函数-第一个参数代表测试套件的标题,第二个参数代表实现测试套件的功能。

//This is test suite
describe("Test Suite", function() {
  	//.....
});

2.2。规格

Jasmine规范表示测试套件中的一个测试用例。首先用it两个参数调用Jasmine全局函数–第一个参数代表规范的标题,第二个参数代表实现测试用例的函数。

实际上,规范包含一个或多个期望。每个期望代表一个可以为true或的断言false。为了通过规范,规范内的所有期望都必须为true。如果规格内的一个或多个期望为false,则规格将失败。

//This is test suite
describe("Test Suite", function() {
  	it("test spec", function() {
   		expect( expression ).toEqual(true);
  	});	
});

让我们开始编写单元测试MathUtils.js以更好地了解套件和规格。我们将在中编写这些规范spec/MathUtils.js

describe("MathUtils", function() {
	var calc;

	//This will be called before running each spec
	beforeEach(function() {
		calc = new MathUtils();
	});

	describe("when calc is used to peform basic math operations", function(){
		
		//Spec for sum operation
		it("should be able to calculate sum of 3 and 5", function() {
			expect(calc.sum(3,5)).toEqual(8);
		});

		//Spec for multiply operation
		it("should be able to multiply 10 and 40", function() {
			expect(calc.multiply(10, 40)).toEqual(400);
		});

		//Spec for factorial operation for positive number
		it("should be able to calculate factorial of 9", function() {
			expect(calc.factorial(9)).toEqual(362880);
		});
		
		//Spec for factorial operation for negative number
		it("should be able to throw error in factorial operation when the number is negative", function() {
			expect(function() { 
				calc.factorial(-7)
	        }).toThrowError(Error);
		});
		
	});
});

SpecRunner.html在浏览器中打开文件时,将运行规格并在浏览器中呈现结果,如下所示:

茉莉花输出
茉莉花输出

3.设置和拆卸

为了进行安装和拆卸,Jasmine在套件级别提供了两个全局功能,即beforeEach()afterEach()

3.1。beforeEach()

beforeEach函数在调用describe()其中的每个规范之前被调用一次。

3.2。afterEach()

afterEach在每个规格后调用该函数一次。

实际上,spec变量(任何变量)在顶级范围(describe块)中定义,并且初始化代码移入beforeEach函数中。该afterEach函数在继续之前重置变量。这有助于开发人员不要为每个规格重复设置和完成代码。

4.茉莉花描述块

在Jasmine中,describe功能是对相关规格进行分组。string参数用于命名规范集合,并将其与规范串联在一起以形成规范的全名。这有助于在大型套件中查找规格。

好消息是,您也可以具有嵌套describe块。在嵌套的情况下describe,Jasmine在执行规范之前,先按beforeEach顺序执行每个功能,然后执行规范,最后逐步执行每个afterEach功能。

让我们通过一个例子来理解它。替换MathUtilSpecs.js以下代码中的内容:

describe("Nested Describe Demo", function() {
	beforeEach(function() {
		console.log("beforeEach level 1");
	});
	describe("MyTest level2", function() {
		beforeEach(function() {
			console.log("beforeEach level 2");
		});
		describe("MyTest level3", function() {
			beforeEach(function() {
				console.log("beforeEach level 3");
			});
			it("is a simple spec in level3", function() {
				console.log("A simple spec in level 3");
				expect(true).toBe(true);
			});
			afterEach(function() {
				console.log("afterEach level 3");
			});
		});
		afterEach(function() {
			console.log("afterEach level 2");
		});
	});
	afterEach(function() {
		console.log("afterEach level 1");
	});
});

现在,通过SpecRunner.html在浏览器中打开来执行此文件。观察控制台输出,它写为:

之前每个级别1 
之前每个级别2 
之前每个级别3 
3级的简单规格 
之后每个级别3 
之后每个级别2 
之后每个级别1

我建议您在上面的代码中放置更多规范,并查看执行 Stream 程以更好地理解。

5.茉莉花火柴

在第一个示例中,我们看到了toEqualand toThrow函数的用法。它们是匹配器,用于比较任何茉莉花测试的实际输出和预期输出。您就像Java断言一样,如果有帮助的话。

让我们列出所有这些茉莉花匹配器,它们可以帮助您更强大和有意义的测试规格。

匹配器 目的
成为() 如果实际值与期望值的类型和值相同,则通过。它与=== Operator 进行比较
toEqual() 适用于简单的文字和变量;
也应该为对象工作
匹配() 检查值是否匹配字符串或正则表达式
被定义为() 确保定义了属性或值
toBeUndefined() 确保未定义属性或值
toBeNull() 确保属性或值为空。
toBeTruthy() 确保属性或值是 true
ToBeFalsy() 确保属性或值是 false
包含装有() 检查字符串或数组是否包含子字符串或项目。
toBeLessThan() 用于小于的数学比较
toBeGreaterThan() 用于大于的数学比较
toBeCloseTo() 用于精确数学比较
toThrow() 用于测试函数是否引发异常
toThrowError() 用于测试特定的抛出异常

not可以将Jasmine 关键字与每个匹配器的条件一起使用来反转结果。例如

expect(actual).not.toBe(expected);
expect(actual).not.toBeDefined(expected);

6.禁用套件和规格

很多时候,出于各种原因,您可能需要禁用套件-一段时间。在这种情况下,您无需删除代码-只需xdescribemake if的开头添加char 即可xdescribe

这些套件及其内部的任何规范在运行时都会被跳过,因此其结果将不会出现在结果中。

xdescribe("MathUtils", function() {
	//code
});

如果您不想禁用整个套件,而只想禁用某个规格测试,则将x本身放在该规格之前,这次仅跳过此规格。

describe("MathUtils", function() {
	//Spec for sum operation
	xit("should be able to calculate the sum of two numbers", function() {
		expect(10).toBeSumOf(7, 3);
	});
});

7.与茉莉花间谍一起工作

茉莉具有双重测试功能,称为间谍。间谍可以对任何函数进行存根,并跟踪对该函数和所有参数的调用。间谍仅存在于定义它的describeit块中,并且在每个规格后将被删除。要在任何方法上创建间谍,请使用spyOn(object, 'methodName')call。

有两个匹配器toHaveBeenCalled,并toHaveBeenCalledWith应与间谍使用。toHaveBeenCalled如果调用了间谍,匹配器将返回true;否则,将返回true。toHaveBeenCalledWith如果参数列表与对间谍的任何记录调用匹配,则matcher将返回true。

describe("MathUtils", function() {
	var calc;

	beforeEach(function() {
		calc = new MathUtils();
		spyOn(calc, 'sum');
	});

	describe("when calc is used to peform basic math operations", function(){
		
		//Test for sum operation
		it("should be able to calculate sum of 3 and 5", function() {
			//call any method
			calc.sum(3,5);

			//verify it got executed
			expect(calc.sum).toHaveBeenCalled();
			expect(calc.sum).toHaveBeenCalledWith(3,5);
		});

	});
});

上面的示例本质上是最基本的,您也可以使用间谍来验证对内部方法的调用。例如,如果您calculateInterest()在任何对象上调用方法,则可能要检查是否存在getPrincipal()getROI()并且getTime()必须已经在该对象内调用了。间谍将帮助您验证这些假设。

当没有要监视的功能时,jasmine.createSpy可以创建一个裸露的间谍。该间谍的行为与其他任何间谍一样-跟踪调用,参数等。但是它背后没有实现。间谍是JavaScript对象,可以这样使用。通常,这些间谍在需要时用作其他函数的回调函数。

var callback = jasmine.createSpy('callback');

//Use it for testing
expect(object.callback).toHaveBeenCalled();

如果需要定义多个此类方法,则可以使用快捷方式方法jasmine.createSpyObj。例如

tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);
tape.play();

//Use it for testing
 expect(tape.play.calls.any()).toEqual(true);

跟踪间谍的每个呼叫并在该calls属性上公开。让我们看看如何使用这些属性来跟踪间谍。

追踪属性 目的
.calls.any() 如果根本没有调用过该间谍,则返回false;如果至少发生一次调用,则返回true。
.calls.count() 返回间谍被调用的次数
.calls.argsFor(index) 返回传递给电话号码索引的参数
.calls.allArgs() 返回所有调用的参数
.calls.all() 返回上下文(this),参数传递所有调用
.calls.mostRecent() 返回this最近调用的上下文()和参数
.calls.first() 返回this第一次调用的上下文()和参数
.calls.reset() 清除所有跟踪以发现间谍

8.茉莉花教程–最终想法

Jasmine是用于测试javascript函数的非常强大的框架,但是学习曲线有点困难。在使用Jasmine进行有效测试之前,需要编写大量实际的javascript代码。

请记住,Jasmine旨在用于以BDD(行为驱动的开发)风格编写测试。不要通过测试无关的东西来滥用它。

让我知道您对这个面向初学者的茉莉花教程的想法。

saigon has written 1445 articles

Leave a Reply