后端与前端测试全解析:从Express到Angular
1. 后端测试:Express控制器测试
1.1 测试准备与思路
在进行Express控制器测试时,我们有两种选择:直接测试控制器方法或使用定义好的Express路由进行测试。由于路由定义较为简单,我们选择后者以编写更全面的测试。
1.2 创建测试文件
首先,在
app/tests
文件夹中创建一个名为
articles.server.controller.tests.js
的新文件,并粘贴以下代码:
const app = require('../../server');
const request = require('supertest');
const should = require('should');
const mongoose = require('mongoose');
const User = mongoose.model('User');
const Article = mongoose.model('Article');
let user, article;
describe('Articles Controller Unit Tests:', () => {
beforeEach((done) => {
user = new User({
firstName: 'Full',
lastName: 'Name',
displayName: 'Full Name',
email: 'test@test.com',
username: 'username',
password: 'password'
});
user.save(() => {
article = new Article({
title: 'Article Title',
content: 'Article Content',
user: user
});
article.save((err) => {
done();
});
});
});
describe('Testing the GET methods', () => {
it('Should be able to get the list of articles', (done) => {
request(app).get('/api/articles/')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
res.body.should.be.an.Array().and.have.lengthOf(1);
res.body[0].should.have.property('title', article.title);
res.body[0].should.have.property('content', article.content);
done();
});
});
it('Should be able to get the specific article', (done) => {
request(app).get('/api/articles/' + article.id)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
res.body.should.be.an.Object().and.have.property('title', article.title);
res.body.should.have.property('content', article.content);
done();
});
});
});
afterEach((done) => {
Article.remove().exec();
User.remove().exec();
done();
});
});
1.3 代码解释
- 依赖引入 :引入所需的模块,包括Express应用、SuperTest、Should.js、Mongoose等。
-
全局变量定义
:定义
user和article全局变量。 -
beforeEach方法 :在每个测试用例执行前,创建新的用户和文章对象并保存到数据库。 -
测试GET方法
:
-
获取文章列表
:使用SuperTest发送HTTP GET请求到
/api/articles/,验证响应的内容类型、状态码和响应体。 -
获取特定文章
:使用SuperTest发送HTTP GET请求到
/api/articles/${article.id},验证响应的内容类型、状态码和响应体。
-
获取文章列表
:使用SuperTest发送HTTP GET请求到
-
afterEach方法 :在每个测试用例执行后,清理Article和User集合。
1.4 运行Mocha测试
运行Mocha测试需要使用之前安装的Mocha命令行工具。操作步骤如下:
1. 使用命令行工具导航到项目的根文件夹。
2. 执行以下命令:
-
Linux/Mac
:
$ NODE_ENV=test mocha --reporter spec app/tests
-
Windows
:
- 先执行
> set NODE_ENV=test
- 再执行
> mocha --reporter spec app/tests
1.5 测试流程
graph LR
A[开始] --> B[准备测试环境]
B --> C[执行测试用例]
C --> D[清理测试数据]
D --> E[输出测试结果]
E --> F[结束]
2. 前端测试:Angular应用测试
2.1 前端测试概述
过去,前端代码测试是一项复杂的任务,跨浏览器和平台运行测试很困难,且大部分应用代码结构不清晰,测试工具主要关注UI端到端测试。但随着MVC框架的发展,Angular团队开发的每个功能都考虑了可测试性,同时平台碎片化催生了测试运行器,方便开发者在不同环境和平台运行测试。
2.2 Jasmine测试框架
Jasmine是由Pivotal组织开发的BDD框架,使用与Mocha BDD接口相同的术语,如
describe()
、
it()
、
beforeEach()
和
afterEach()
。与Mocha不同的是,Jasmine自带断言功能,使用
expect()
方法链式调用匹配器(Matchers)。
2.2.1 匹配器示例
describe('Matchers Example', function() {
it('Should present the toBe matcher example', function() {
var a = 1;
var b = a;
expect(a).toBe(b);
expect(a).not.toBe(null);
});
});
toBe()
匹配器使用
===
运算符比较对象,Jasmine还包含许多其他匹配器,甚至允许开发者添加自定义匹配器。
2.3 Karma测试运行器
Karma是Angular团队开发的工具,可帮助开发者在不同浏览器中执行测试。它通过启动一个Web服务器,在选定的浏览器上运行源代码和测试代码,并将测试结果返回给命令行工具。
2.3.1 安装Karma命令行工具
在命令行中执行以下命令全局安装Karma命令行工具:
$ npm install -g karma-cli
2.3.2 安装Karma的依赖
修改
package.json
文件,添加Karma和Jasmine相关依赖:
{
"name": "MEAN",
"version": "0.0.10",
"scripts": {
"tsc": "tsc",
"tsc:w": "tsc -w",
"app": "node server",
"start": "concurrently \"npm run tsc:w\" \"npm run app\" ",
"postinstall": "typings install"
},
"dependencies": {
"@angular/common": "2.1.1",
"@angular/compiler": "2.1.1",
"@angular/core": "2.1.1",
"@angular/forms": "2.1.1",
"@angular/http": "2.1.1",
"@angular/platform-browser": "2.1.1",
"@angular/platform-browser-dynamic": "2.1.1",
"@angular/router": "3.1.1",
"body-parser": "1.15.2",
"core-js": "2.4.1",
"compression": "1.6.0",
"connect-flash": "0.1.1",
"connect-mongo": "1.3.2",
"cookie-parser": "1.4.3",
"ejs": "2.5.2",
"express": "4.14.0",
"express-session": "1.14.1",
"method-override": "2.3.6",
"mongoose": "4.6.5",
"morgan": "1.7.0",
"passport": "0.3.2",
"passport-facebook": "2.1.1",
"passport-google-oauth": "1.0.0",
"passport-local": "1.0.0",
"passport-twitter": "1.0.4",
"reflect-metadata": "0.1.8",
"rxjs": "5.0.0-beta.12",
"socket.io": "1.4.5",
"systemjs": "0.19.39",
"zone.js": "0.6.26"
},
"devDependencies": {
"concurrently": "3.1.0",
"jasmine": "2.5.2",
"jasmine-core": "2.5.2",
"karma": "1.3.0",
"karma-jasmine": "1.0.2",
"karma-phantomjs-launcher": "1.0.2",
"should": "11.1.1",
"supertest": "2.0.1",
"traceur": "0.0.111",
"typescript": "2.0.3",
"typings": "1.4.0"
}
}
然后在应用的根文件夹中执行
$ npm install
安装新的依赖。
2.3.3 配置Karma测试运行器
在应用的根文件夹中创建一个名为
karma.conf.js
的文件,并粘贴以下代码:
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'node_modules/systemjs/dist/system.js',
'node_modules/systemjs/dist/system-polyfills.js',
'node_modules/core-js/client/shim.min.js',
'node_modules/reflect-metadata/Reflect.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/proxy.js',
'node_modules/zone.js/dist/sync-test.js',
'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/fake-async-test.js',
{ pattern: 'public/systemjs.config.js', served: true, included: false, watched: false },
{ pattern: 'public/app/**/*.*', served: true, included: false, watched: false },
{ pattern: 'node_modules/rxjs/**/*.js', served: true, included: false, watched: false },
{ pattern: 'node_modules/@angular/**/*.js', served: true, included: false, watched: false },
'karma.shim.js'
],
proxies: {
'/lib/': '/base/node_modules/',
'/app/': '/base/public/app/'
},
reporters: ['progress'],
browsers: ['PhantomJS'],
captureTimeout: 60000,
singleRun: true
});
};
2.4 配置说明
| 配置项 | 说明 |
|---|---|
basePath
| Karma使用的基础路径,这里为空 |
frameworks
| 使用的测试框架,这里是Jasmine |
files
| Karma测试时包含的文件列表,可使用通配符 |
proxies
| 代理配置,用于映射路径 |
reporters
|
测试结果报告方式,这里是
progress
|
browsers
|
测试使用的浏览器,这里是
PhantomJS
|
captureTimeout
| 捕获浏览器的超时时间 |
singleRun
| 是否在测试完成后退出 |
2.5 修改System.js配置
打开
public/systemjs.config.js
文件,修改如下:
(function(global) {
var packages = {
app: {
main: './bootstrap.js',
defaultExtension: 'js'
}
};
var map = {
'@angular': 'lib/@angular',
'rxjs': 'lib/rxjs'
};
var ngPackageNames = [
'common',
'compiler',
'core',
'forms',
'http',
'router',
'platform-browser',
'platform-browser-dynamic'
];
ngPackageNames.forEach(function(pkgName) {
packages['@angular/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
map['@angular/' + pkgName + '/testing'] = 'lib/@angular/' + pkgName + '/bundles/' + pkgName + '-testing.umd.js';
});
System.config({
defaultJSExtensions: true,
transpiler: null,
packages: packages,
map: map
});
})(this);
2.6 创建karma.shim.js文件
在应用的根文件夹中创建一个名为
karma.shim.js
的文件,并粘贴以下代码:
__karma__.loaded = function () { };
System.import('/base/public/systemjs.config.js').then(loadTests);
function loadTests() {
Promise.all([
System.import('app/bootstrap.spec'),
System.import('app/articles/articles.service.spec'),
System.import('app/articles/list/list.component.spec'),
System.import('app/app.routes.spec'),
System.import('app/directive.spec'),
System.import('app/pipe.spec')
]).then(__karma__.start, __karma__.error);
}
2.7 编写Angular单元测试
完成测试环境的配置后,编写单元测试就变得容易了。虽然总体结构相同,但每个实体的测试都有细微差别。下面将介绍如何测试主要的Angular实体。
2.7.1 测试组件
以测试文章列表组件为例,该组件使用了服务并渲染了简单的DOM。操作步骤如下:
1. 进入
public/app/articles/list
文件夹,创建一个名为
list.component.spec.ts
的文件。
2. 在新文件中粘贴以下代码:
import { Observable } from "rxjs/Rx";
import { Directive, Input } from '@angular/core';
import { ComponentFixture, TestBed, async, fakeAsync } from '@angular/core/testing';
import { ArticlesService } from '../articles.service';
import { ListComponent } from './list.component';
class MockArticlesService {
articles = [{
_id: '12345678',
title: 'An Article about MEAN',
content: 'MEAN rocks!',
created: new Date(),
creator: {
fullName: 'John Doe'
}
}];
public list() {
return Observable.of(this.articles);
}
};
@Directive({
selector: '[routerLink]',
host: {
'(click)': 'onClick()'
}
})
export class RouterLinkStubDirective {
@Input('routerLink') linkParams: any;
navigatedTo: any = null;
onClick() {
this.navigatedTo = this.linkParams;
}
}
describe('List component tests', () => {
let componentFixture: ComponentFixture<ListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ListComponent, RouterLinkStubDirective ],
providers: [ {provide: ArticlesService, useClass: MockArticlesService } ]
}).compileComponents();
}));
beforeEach(fakeAsync(() => {
componentFixture = TestBed.createComponent(ListComponent);
}));
it('Should render list', () => {
componentFixture.detectChanges();
const mockArticleService = new MockArticlesService();
const listComponentElement = componentFixture.nativeElement;
const articleElements = listComponentElement.querySelectorAll('li');
const articleElement = articleElements[0];
const articleTitleElement = articleElement.querySelector('a');
const articleContentElement = articleElement.querySelector('p');
const mockArticleList = mockArticleService.articles;
const mockArticle = mockArticleList[0];
const mockArticleTitle = mockArticle.title;
// 后续可添加更多断言
});
});
2.7.2 代码解释
-
模拟服务
:创建
MockArticlesService类,模拟ArticlesService的list方法,返回一个包含文章数据的可观察对象。 -
模拟指令
:创建
RouterLinkStubDirective指令,模拟routerLink指令的行为。 -
测试配置
:使用
TestBed.configureTestingModule配置测试模块,声明组件和模拟指令,提供模拟服务。 -
测试用例
:在
it方法中,检测组件的变更,获取组件的DOM元素,验证渲染结果。
2.8 Angular单元测试流程
graph LR
A[开始] --> B[配置测试环境]
B --> C[编写测试用例]
C --> D[运行测试]
D --> E[分析测试结果]
E --> F[修复问题(如有)]
F --> D
E --> G[结束]
3. 总结
本文详细介绍了后端Express控制器测试和前端Angular应用测试的方法和步骤。在后端测试中,我们使用Mocha和SuperTest对Express控制器的GET方法进行了测试,并通过清理测试数据保证测试环境的独立性。在前端测试中,我们使用Jasmine作为测试框架,Karma作为测试运行器,对Angular组件进行了单元测试。通过这些测试方法,可以提高代码的质量和可靠性,减少潜在的问题。
在实际开发中,建议从项目开始就建立测试规范,随着项目的发展不断完善测试用例,以确保应用的稳定性和可维护性。同时,可以根据项目的需求,扩展测试工具和框架的配置,如添加更多的浏览器启动器、使用不同的测试报告方式等。
超级会员免费看
961

被折叠的 条评论
为什么被折叠?



