23、后端与前端测试全解析:从Express到Angular

后端与前端测试全解析:从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} ,验证响应的内容类型、状态码和响应体。
  • 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组件进行了单元测试。通过这些测试方法,可以提高代码的质量和可靠性,减少潜在的问题。

在实际开发中,建议从项目开始就建立测试规范,随着项目的发展不断完善测试用例,以确保应用的稳定性和可维护性。同时,可以根据项目的需求,扩展测试工具和框架的配置,如添加更多的浏览器启动器、使用不同的测试报告方式等。

【无人机】基于改进粒子群算法的无人机路径规划研究[和遗传算法、粒子群算法进行比较](Matlab代码实现)内容概要:本文围绕基于改进粒子群算法的无人机路径规划展开研究,重点探讨了在复杂环境中利用改进粒子群算法(PSO)实现无人机三维路径规划的方法,并将其遗传算法(GA)、标准粒子群算法等传统优化算法进行对比分析。研究内容涵盖路径规划的多目标优化、避障策略、航路点约束以及算法收敛性和寻优能力的评估,所有实验均通过Matlab代码实现,提供了完整的仿真验证流程。文章还提到了多种智能优化算法在无人机路径规划中的应用比较,突出了改进PSO在收敛速度和局寻优方面的优势。; 适合人群:具备一定Matlab编程基础和优化算法知识的研究生、科研人员及从事无人机路径规划、智能优化算法研究的相关技术人员。; 使用场景及目标:①用于无人机在复杂地形或动态环境下的三维路径规划仿真研究;②比较不同智能优化算法(如PSO、GA、蚁群算法、RRT等)在路径规划中的性能差异;③为多目标优化问题提供算法选型和改进思路。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注算法的参数设置、适应度函数设计及路径约束处理方式,同时可参考文中提到的多种算法对比思路,拓展到其他智能优化算法的研究改进中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值