作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
米哈伊尔·安格诺夫
验证专家 在工程
21 Years 的经验

米哈伊尔拥有物理学硕士学位. 他和诺德关系很好.js, Go, JavaScript spa, React.. js, Flux/Redux, RIOT.js和AngularJS.

专业知识

Share

测试是构建健壮Node的重要组成部分.js应用程序. 适当的测试可以很容易地克服开发人员可能指出的许多缺点 Node.Js开发方案.

虽然许多开发人员关注单元测试的100%覆盖率, 重要的是,您编写的代码不是单独测试的. 集成和端到端测试通过一起测试应用程序的各个部分,为您提供了额外的信心. 这些部件可能自己工作得很好, 但是在一个大系统中, 代码单元很少单独工作.

Node.js和MongoDB一起构成了最近最受欢迎的二人组之一. 如果你恰好是使用它们的许多人中的一员,那么你很幸运.

在本文中,您将学习如何轻松地为Node编写集成和端到端测试.在数据库的真实实例上运行的MongoDB应用程序,而不需要设置一个复杂的环境或复杂的设置/拆除代码.

您将看到mongo-unit包如何帮助在Node中进行集成和端到端测试.js. 以获取更全面的Node概述.Js集成测试,参见 这篇文章.

处理真实的数据库

通常, 用于集成或端到端测试, 您的脚本将需要连接到一个真正的专用数据库以进行测试. 这包括编写在每个测试用例/套件的开始和结束处运行的代码,以确保数据库处于干净的可预测状态.

这可能对某些项目很有效,但也有一些局限性:

  • 测试环境可能相当复杂. 您需要在某个地方保持数据库运行. 这通常需要额外的工作来设置CI服务器.
  • 数据库和操作可能相对较慢. 因为数据库将使用网络连接,操作将需要文件系统活动, 快速运行数千个测试可能并不容易.
  • 数据库保存状态,这对于测试来说不是很方便. 测试应该彼此独立,但是使用公共DB可能会使一个测试影响其他测试.

另一方面, 使用真实的数据库使测试环境尽可能接近生产环境. 这可以看作是这种方法的一个特殊优势.

使用真实的内存数据库

使用真实的数据库进行测试似乎有一些挑战. 但是,使用真实数据库的优势太好了,无法传递. 我们如何应对挑战并保持优势?

重用来自另一个平台的好的解决方案并将其应用于Node.Js的世界可以是这里的路.

Java项目广泛使用DBUnit和内存数据库.g.(H2)用于此目的.

DBUnit与JUnit (Java测试运行器)集成,并允许您定义每个测试/测试套件的数据库状态, etc. 它消除了上面讨论的约束:

  • DBUnit和H2是Java库,因此不需要设置额外的环境. 这一切都在JVM中运行.
  • 内存中的数据库使得这种状态管理非常快.
  • DBUnit使数据库配置非常简单,并允许您为每种情况保持清晰的数据库状态.
  • H2是一个SQL数据库,它与MySQL部分兼容, 在重大案件中, 应用程序可以像使用生产数据库一样使用它.

从这些概念出发,我决定为Node做一些类似的东西.. js和MongoDB: Mongo-unit.

Mongo-unit是一个Node.可以使用NPM或Yarn安装. 它在内存中运行MongoDB. 通过与Mocha很好地集成并提供一个简单的API来管理数据库状态,它使集成测试变得容易.

图书馆使用 mongodb-prebuilt NPM包,其中包含为流行操作系统预构建的MongoDB二进制文件. 这些MongoDB实例可以在内存模式下运行.

安装Mongo-unit

要将mongo-unit添加到你的项目中,你可以运行:

npm install -D mongo-unit

or

纱线添加蒙古单位

就是这样. 您甚至不需要在计算机上安装MongoDB就可以使用这个包.

使用蒙古包单元进行集成测试

假设您有一个简单的Node.Js应用程序来管理任务:

/ /服务.js

Const 猫鼬 = 要求(“猫鼬')
const mongoUrl =进程.env.MONGO_URL || 'mongodb://localhost:27017/example'
猫鼬.连接(mongoUrl)
const TaskSchema = new 猫鼬.模式({
 名称:字符串,
 :开始日期,
 完成:布尔,
})
const任务=猫鼬.模型(“任务”,TaskSchema)

module.出口= {
 getTasks: () => Task.find(),
 addTask: data => new Task(data).save(),
 deleteTask: taskId => Task.findByIdAndRemove (taskId)
}

MongoDB连接URL在这里不是硬编码的. 与大多数web应用程序后端一样,我们从环境变量中获取它. 这将允许我们在测试期间将其替换为任何URL.

Const express = 要求(“express')
const bodyParser = 要求(“body-parser')
Const service = 要求(“./服务”)
Const app = express()
app.使用(bodyParser.json())

app.使用(表达.静态的(“$ {__dirname} /静态”))
app.get('/example', (req, res) => {
 service.getTasks ().then(tasks => res.json(任务))
})
app.post('/example', (req, res) => {
 service.addTask(要求.body).then(data => res.json(数据))
})
app.delete('/example/:taskId', (req, res) => {
 service.deleteTask(要求.params.taskId).then(data => res.json(数据))
})
app.listen(3000, () => console.日志('在端口3000启动'))

这是一个具有用户界面的示例应用程序的代码片段. 为简洁起见,省略了UI的代码. 您可以查看完整的示例 GitHub上.

与摩卡集成

让Mocha运行针对mongo-unit的集成测试, 我们需要在将应用程序代码加载到Node之前运行mongo-unit数据库实例.js上下文. 要做到这一点,我们可以使用 摩卡,需要 参数和Mocha-prepare库, 哪一个允许您在require脚本中执行异步操作.

/ / it-helper.js
Const prepare = 要求(“mocha-prepare')
const monounit = 要求(“mongo-unit')

prepare(done => mongoUnit.start()
 .then(testMongoUrl => {
   process.env.MONGO_URL = testmongodb
   done()
 }))

编写集成测试

第一步是将测试添加到测试数据库(testData.json):

{
   “任务”:[
   {
     “名称”:“测试”,
     “开始”:“2017 - 08 - 28 - t16:07:38.268Z",
     “完成”:假的
   }
 ]
}

下一步是添加测试本身:

Const expect = 要求(“chai').expect
Const 猫鼬 = 要求(“猫鼬')
const monounit = 要求(“../指数”)
Const service = 要求(“./应用程序/服务”)
const testMongoUrl =进程.env.MONGO_URL

describe('service', () => {
 const testData = 要求(“./夹具/ testData.json')
 beforeEach(() => mongoUnit.initDb (testMongoUrl testData))
 afterEach(() => mongoUnit.drop())

 it('should find all tasks', () => {
   返回服务.getTasks ()
     .then(tasks => {
       期望(任务.length).to.= (1)
       期望(任务[0].name).to.平等(测试)
     })
 })

 it('should create new task', () => {
   返回服务.addTask({name: 'next', completed: false})
     .then(task => {
       期望(任务.name).to.平等(下)
       期望(任务.完成).to.平等的(错误的)
     })
     .then(() => service.getTasks ())
     .then(tasks => {
       期望(任务.length).to.等于(2)
       期望(任务[1].name).to.平等(下)
     })
 })

 it('should remove task', () => {
   返回服务.getTasks ()
     .then(tasks => tasks[0]._id)
     .then(taskId => service.deleteTask (taskId))
     .then(() => service.getTasks ())
     .then(tasks => {
       期望(任务.length).to.平等(0)
     })
 })
})

而且,瞧!

请注意,这里处理设置和删除的代码只有几行.

正如您所看到的,使用mongo-unit库编写集成测试非常容易. 我们不模拟MongoDB本身,我们可以使用相同的Mongoose模型. 我们完全控制了数据库数据,并且在测试性能上没有损失太多 fake MongoDB在内存中运行.

这也允许我们将最佳单元测试实践应用于集成测试:

  • 使每个测试独立于其他测试. 我们在每次测试之前加载新数据,为每次测试提供完全独立的状态.
  • 对每个测试使用最小要求状态. 我们不需要填充整个数据库. 我们只需要为每个特定的测试设置所需的最小数据.
  • 我们可以为数据库重用一个连接. 它提高了测试性能.

作为奖励,我们甚至可以在mongo-unit上运行应用程序本身. 它允许我们针对模拟数据库对应用程序进行端到端测试.

端到端测试

对于端到端测试,我们将使用 硒WebDriver and 赫敏E2E测试员.

首先,我们将引导驱动程序和测试运行程序:

const monounit = 要求(“mongo-unit')
Const selenium = 要求(“selenium-standalone')
const Hermione = 要求(“ Hermione ')
新赫敏()./ e2e赫敏.conf.Js’)//赫敏配置

seleniumInstall() //确保安装了selenium
 .然后(seleniumStart) //启动selenium web驱动程序
 .然后(mongoUnit.Start) //启动mongo单元
 .then(testMongoUrl => {
   process.env.MONGO_URL = testmongodb //存储mongo的url
 })
 .then(() => {
   要求(“./index.Js’)//启动应用程序
 })
 .然后(delay(1000)) //等待一秒钟,直到应用程序启动
 .then(() => hermione.run(", heroneopts)) //运行heronee2e测试
 .then(() => process.exit(0))
 .catch(() => process.exit(1))

我们还需要一些辅助函数(为了简洁,删除了错误处理):

函数seleniumInstall() {
 return new Promise(resolve => selenium.解决安装({}))
}

函数seleniumStart() {
 return new Promise(resolve => selenium.开始(解决)
}

函数delay(timeout) {
 return new Promise(resolve => setTimeout(resolve, timeout))
}

用一些数据填充数据库,并在测试完成后清理数据库, 我们可以运行第一个测试:

Const expect = 要求(“chai').expect
Const co = 要求(“co')
const monounit = 要求(“../指数”)
const testMongoUrl =进程.env.MONGO_URL
const DATA = 要求(“./夹具/ testData.json')

Const UI = {
 task: '.task',
 删除:“.task .删除的,
 名称:“#名称”,
 日期:“#日期”,
 addTask:“# addTask”
}

describe('Tasks', () => {

 beforeEach(function () {
   返回mongoUnit.initDb (testMongoUrl、数据)
     .then(() => this.browser.url (http://localhost: 3000))
 })

 afterEach(() => mongoUnit.dropDb (testMongoUrl))

 它('应该显示任务列表',function () {
   Const browser = this.browser
   返回co(function* () {
     Const任务= yield浏览器.(ui元素.task)
     期望(任务.长度,1)
   })
 })

 It ('should create task', function () {
   Const browser = this.browser
   返回co(function* () {
     收益率的浏览器.ui元素(.name).setValue(测试)
     收益率的浏览器.ui元素(.addTask).click()
     Const任务= yield浏览器.(ui元素.task)
     期望(任务.长度,2)
   })
 })

 It ('should remove task', function () {
   Const browser = this.browser
   返回co(function* () {
     收益率的浏览器.ui元素(.remove).click()
     Const任务= yield浏览器.(ui元素.task)
     期望(任务.长度,0)
   })
 })
})

正如您所看到的,端到端测试看起来与集成测试非常相似.

Wrap Up

集成和端到端测试对于任何大型应用程序都很重要. Node.特别是Js应用程序,可以从自动化测试中获益良多. 与mongo-unit, 您可以编写集成和端到端测试,而不必担心这些测试带来的所有挑战.

你可以找到 完整的例子 如何在GitHub上使用mongo-unit.

了解基本知识

  • 什么是集成测试?

    集成测试是一种自动化测试,用于验证系统的多个组件在各种情况下是否正确工作.

  • E2E代表什么?

    E2E是端到端(end- end)的缩写,通常用于端到端测试的上下文中.

聘请Toptal这方面的专家.
现在雇佣
米哈伊尔·安格诺夫

米哈伊尔·安格诺夫

验证专家 在工程
21 Years 的经验

下诺夫哥罗德,下诺夫哥罗德州,俄罗斯

2015年7月6日加入

作者简介

米哈伊尔拥有物理学硕士学位. 他和诺德关系很好.js, Go, JavaScript spa, React.. js, Flux/Redux, RIOT.js和AngularJS.

作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

专业知识

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 隐私政策.

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 隐私政策.

Toptal开发者

加入总冠军® 社区.