使用 DB Fixtures 为 Unit Test 提供基础数据,Sails + Mocha 实现。

问题:Test Fixture 太分散,管理麻烦。

在做单元测试的时候,数据回滚是个比较麻烦的问题。

比较简单的解决方案是,在单元测试的时候连接单独的 DB ,测试完毕后清空 DB 内容即可。但是这带来另一个问题,DB 一开始是空的,在测试一个 case 的时候,我们需要提供这个 case 的需要基础数据,做一些初始化操作,这类操作叫做 Test Fixture,这个概念是什么呢?这篇文章有稍微解释http://magustest.com/blog/whiteboxtesting/three-different-test-fixture-setup-approach/。

就是“就是运行被测软件所需要的一切东西”。


在 Mocha 中提供 Test Fixture 是 before 方法,见官网http://mochajs.org/

  1. describe('hooks', function() {
  2. before(function(cb) {
  3. // runs before all tests in this block
  4. User.create(user,function(err,data){
  5. user_id = data.id;
  6. cb(err);
  7. });
  8. });
  9. after(function() {
  10. // runs after all tests in this block
  11. });
  12. // test cases
  13. });

在 before 里 create 一个对象,完成数据准备,然后在 test cases 中就可以使用这个对象了。这是比较简单 Test Fixture 实现,但是在 Test Case 比较多时候,会带来一些问题。

  1. 数据重复,每个 case 之间无法共享数据, 在 case A 里初始化了一个 User 对象,在 case B 要再初始化一次。
  2. 数据维护管理混乱,这些数据分散在各个 case 中,无法集中管理。而且类似 User 这样的对象结构变更了,还需要更新每个 case 中对 User 初始化的代码。

    会发现 before 经常在做些重复工作,例如我在这个 case 中 create 了一个 User 对象,在另一个 case 依旧需要 create 一个 User 对象。

解决方案:集中管理 Test Fixture

Sails 已经提供了工具包实现来解决上述问题, Barrels https://www.npmjs.com/package/barrels

Simple DB Fixtures for Sails.js with associations support 。在 Github 中提供了该工具的 Demo

https://github.com/bredikhin/barrels/blob/master/test/index.test.js,可以参考。

Barrels支持将所有基础数据定义成 json 文件,然后在 Unit Test 环境初始化的时候使用 Barrels load 这些 json 并持久化到测试 DB 中。

在 Sails 中 这些文件默认存放在 test/fixtures 目录中。

  1. test
  2. --fixtures
  3. --user.json
  4. --order.json
  5. --bootstrap.test.js

以 user.json 为例,它是长这样的,是一个数组。

  1. [
  2. {
  3. "name": "Walter White",
  4. "email": "walter@heisenberg.com"
  5. },
  6. {
  7. "name": "John Appleseed",
  8. "email": "appleseed@me.com"
  9. }
  10. ]

Sails中, test 目录 bootstrap.test.js 文件是 Sails 用来定义Unit Test 初始化环境的文件,我们在该文件里添加如下代码完成 Test Fixtures 的初始化

  1. before(function (done) {
  2. Sails.lift({
  3. log: {
  4. level: 'error'
  5. },
  6. models: {
  7. connection: 'testMongodb',
  8. migrate: 'drop'
  9. }
  10. }, function(err, sails) {
  11. if (err)
  12. return done(err);
  13. // Load fixtures
  14. var barrels = new Barrels();
  15. // 初始化测试环境上下文
  16. TestContext.loadData(barrels);
  17. // Populate the DB
  18. barrels.populate(function(err) {
  19. done(err, sails);
  20. });
  21. });
  22. });

在 new Barrels() 时会载入所有 json 文件,然后使用 barrels.populate 方法将 json 持久化到 DB 中。

OK,这样就可以在 test/fixtures 目录统一管理测试数据了。

那么,真正的问题来,如何在 test case 中获取这些 json 对象呢?

在 Test Case 中获取 Test Fixtures

Barrels 将所有 json 文件存储在自身的 data 属性中,要获取 user 列表,可以这样:

  1. var barrels = new Barrels();
  2. var userList = barrels.data.user

那怎么获取具体的一个 user 呢?

通过 user_id 在数据库中查询出来可以吗?像这样:

  1. before(function (done) {
  2. User.findOne({id: user_id}, function (err, user) {
  3. cb(err, user)
  4. });
  5. });

每次都要查询会不会太麻烦?而且怎么知道哪个 user_id 对应哪位 user 呢?之前说好的数据共享去哪里了呢?

使用测试上下文 TestContext 来共享数据。

上述问题有两个:

  1. 怎么指定特定的 user
  2. 怎么共享数据

解决第一个问题,可以在 user.json 文件中为每一个 user 对象指定一个 key :

  1. [
  2. {
  3. "key": "base",
  4. "name": "Walter White",
  5. "email": "walter@heisenberg.com"
  6. },
  7. {
  8. "key": "john",
  9. "name": "John Appleseed",
  10. "email": "appleseed@me.com"
  11. }
  12. ]

解决第二个问题,自己定义一个 TestContext 对象,在 bootstrap.test.js 加入以下代码来初始化 TestContext:

  1. // 初始化测试环境上下文
  2. TestContext.loadData(barrels);

TestContext.loadData的实现:

  1. var _ = require('underscore');
  2. module.exports = {
  3. loadData: function (barrels) {
  4. self.barrels = barrels;
  5. var fixtures = barrels.data;
  6. self.fixtures = fixtures;
  7. // 遍历每个model
  8. _.each(Object.keys(fixtures), function (key) {
  9. //遍历每个儿对象
  10. _.each(fixtures[key], function (object) {
  11. if (!self[key]) {
  12. self[key] = {};
  13. }
  14. if (object.key) {
  15. // barrels.model.key = object
  16. self[key][object.key] = object;
  17. }
  18. });
  19. });
  20. }
  21. };
  22. var self = module.exports;

再做了这些初始化操作之后,在 Test Case 中,就可以这样获取一个 User 对象了:

  1. var TestContext = require('../lib/TestContext');
  2. describe("Unit TEST !", function () {
  3. var user = TestContext.user.base;
  4. before(function (done) {
  5. done();
  6. });
  7. describe(" do test ", function () {
  8. it(" do test ", function (done) {
  9. // 测试逻辑
  10. done(err);
  11. });
  12. });
  13. }

非常方便,引入 TestContext 对象后就可以获取 user 对象了, base 就是 user.json 中某位 user 定义的 key 。这样上述的两个问题就完美解决啦。

还可以做点什么

Barrels 的功能是 load json 并持久化数据,除了用于单元测试环境的初始化,也能用于开发或者正式环境的初始化。在项目部署的时候,总是需要部署一些基础默认数据的,可以用 Barrels 来完成这个工作。

在某个文件夹中定义好 json 文件,在项目初始化时加入以下代码:

  1. // Load fixtures
  2. var barrels = new Barrels('josn 文件目录');
  3. // Populate the DB
  4. barrels.populate(function(err) {
  5. done(err);
  6. });

这样就能完成初始化啦。

一定要注意的问题, Barrels 在保存数据之前会清空表,小心数据丢失。或者可以修改 Barrels 的代码,在清空表的逻辑去掉,这样就一定安全啦。

使用 DB Fixtures 为 Unit Test 提供基础数据,Sails + Mocha 实现。的更多相关文章

  1. RPC 编程 使用 RPC 编程是在客户机和服务器实体之间进行可靠通信的最强大、最高效的方法之一。它为在分布式计算环境中运行的几乎所有应用程序提供基础。

    RPC 编程 使用 RPC 编程是在客户机和服务器实体之间进行可靠通信的最强大.最高效的方法之一.它为在分布式计算环境中运行的几乎所有应用程序提供基础.本文介绍 RPC 客户机和服务器之间基本的事件流 ...

  2. iOS基础 - 数据存取

    一.iOS应用数据存储的常用方式 XML属性列表(plist)归档 Preference(偏好设置) NSKeyedArchiver归档 SQLite3 Core Data 二.应用沙盒 每个iOS应 ...

  3. 利用Python进行数据分析(12) pandas基础: 数据合并

    pandas 提供了三种主要方法可以对数据进行合并: pandas.merge()方法:数据库风格的合并: pandas.concat()方法:轴向连接,即沿着一条轴将多个对象堆叠到一起: 实例方法c ...

  4. C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 远程同步服务器大量基础数据到客户端

    服务器上保存有上万条的基础数据,需要同步到全国各地的成千上万个客户端,而且这些基础数据也经常在有变化调整.这时候需要有一个稳定的数据同步程序,能分批同步基础数据,由于网络流量,网络的稳定性等因素,需要 ...

  5. [.net 面向对象编程基础] (4) 基础中的基础——数据类型转换

    [.net面向对象编程基础] (4)基础中的基础——数据类型转换 1.为什么要进行数据转换? 首先,为什么要进行数据转换,拿值类型例子说明一下, 比如:我们要把23角零钱,换成2.30元,就需要把整形 ...

  6. 解密 Uber 数据团队的基础数据架构优化之路

    如果你用过Uber,你一定会注意到它的操作是如此的简单.你一键叫车,随后车就来找你了,最后自动完成支付,整个过程行云流水.但是,在这简单的流程背后其实是用Hadoop和Spark这样复杂的基础大数据架 ...

  7. elasticsearch 导入基础数据并索引之 geo_point

    elasticsearch 中的地理信息存储, 有geo_point形式和geo_shape两种形式 此篇只叙述geo_point, 地理位置需要声明为特殊的类型, 不显示在mapping中定义的话, ...

  8. django “如何”系列8:如何为模型提供初始化数据

    当你第一次配置一个app的时候,有时候使用硬编码的数据去预填充你的数据库是非常有用的.这里有几个你可以让django自动创建这些数据的方法:你可以提供固定格式的初始化数据或者提供通过SQL初始化数据. ...

  9. docker-compose 构建mongodb并导入基础数据示例

    使用docker-compose构建mongodb服务并导入基础数据示例. 1.文件目录结构 ——mongo/ |——docker-compose.yml |——mongo-Dockerfile |— ...

随机推荐

  1. android 实现2张图片层叠效果

    如图: 代码: <RelativeLayout android:layout_width="match_parent" android:layout_height=" ...

  2. jquery $.ajax方法

    $.ajax({ timeout: 3000, cache:false, global:false, type: "POST", url: WEB_URL+"/logic ...

  3. linux 配置ssh免密码登录

    1.确保主机名唯一 主机名修改方法: a.修改/etc/sysconfig/network,HOSTNAME=想要设置的主机名称 b.修改/etc/hosts,127.0.0.1   localhos ...

  4. 各大公司广泛使用的在线学习算法FTRL详解

    各大公司广泛使用的在线学习算法FTRL详解 现在做在线学习和CTR常常会用到逻辑回归( Logistic Regression),而传统的批量(batch)算法无法有效地处理超大规模的数据集和在线数据 ...

  5. 在线最优化求解(Online Optimization)之五:FTRL

    在线最优化求解(Online Optimization)之五:FTRL 在上一篇博文中中我们从原理上定性比较了L1-FOBOS和L1-RDA在稀疏性上的表现.有实验证明,L1-FOBOS这一类基于梯度 ...

  6. jetty启动报错Unsupported major.minor version 51.0

    主要是JDK版本的问题,需要将Eclipse的Jdk版本设置为1.7的才可以,编译级别也设置为1.7,然后删除maven项目路径,D:\WORK\workspace\xxx\target下的所有文件, ...

  7. 单件模式(Singleton Pattern)(转)

    概述 Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点.这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考 ...

  8. 用HAProxy和KeepAlived构建高可用的反向代理

      用HAProxy和KeepAlived构建高可用的反向代理 用HAProxy和KeepAlived构建高可用的反向代理 前言对于访问量较大的网站来说,随着流量的增加单台服务器已经无法处理所有的请求 ...

  9. (转)排列算法 Permutation Generation

    转自:http://www.cnblogs.com/dragonpig/archive/2010/01/21/1653680.html http://www.notesandreviews.com/p ...

  10. iOS-CALayer实现简单进度条

    /** *  用CALayer定制下载进度条控件 *  1.单独创建出CALayer *  2.直接修改CALayer的frame值,执行隐式动画,实现进度条效果 *  3.用定时器(NSTimer) ...