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


    使用 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/

    describe('hooks', function() {
    
      before(function(cb) {
        // runs before all tests in this block
        User.create(user,function(err,data){
          user_id = data.id;
          cb(err);
        });
      });
    
      after(function() {
        // runs after all tests in this block
      });
    
      // test cases
    });
    

    在 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 目录中。

    test
    	--fixtures
    		--user.json
    		--order.json
    		--bootstrap.test.js 
    

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

    [
      {
        "name": "Walter White",
        "email": "walter@heisenberg.com"
      },
      {
        "name": "John Appleseed",
        "email": "appleseed@me.com"
      }
    ]
    

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

    before(function (done) {
      Sails.lift({
        log: {
          level: 'error'
        },
        models: {
          connection: 'testMongodb',
          migrate: 'drop'
        }
      }, function(err, sails) {
        if (err)
          return done(err);
        // Load fixtures
        var barrels = new Barrels();
    
        // 初始化测试环境上下文
        TestContext.loadData(barrels);
    
        // Populate the DB
        barrels.populate(function(err) {
          done(err, sails);
        });
      });
    });
    

    在 new Barrels() 时会载入所有 json 文件,然后使用 barrels.populate 方法将 json 持久化到 DB 中。
    OK,这样就可以在 test/fixtures 目录统一管理测试数据了。

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

    在 Test Case 中获取 Test Fixtures

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

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

    那怎么获取具体的一个 user 呢?
    通过 user_id 在数据库中查询出来可以吗?像这样:

    before(function (done) {
        User.findOne({id: user_id}, function (err, user) {
            cb(err, user)
        });
    });
    

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

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

    上述问题有两个:

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

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

    [
      {
        "key": "base",
        "name": "Walter White",
        "email": "walter@heisenberg.com"
      },
      {
        "key": "john",
        "name": "John Appleseed",
        "email": "appleseed@me.com"
      }
    ]
    

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

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

    TestContext.loadData的实现:

    var _ = require('underscore');
    module.exports = {
    
      loadData: function (barrels) {
        self.barrels = barrels;
        var fixtures = barrels.data;
        self.fixtures = fixtures;
    
        // 遍历每个model
        _.each(Object.keys(fixtures), function (key) {
          //遍历每个儿对象
          _.each(fixtures[key], function (object) {
            if (!self[key]) {
              self[key] = {};
            }
            if (object.key) {
              // barrels.model.key = object
              self[key][object.key] = object;
            }
          });
        });
      }
    };
    var self = module.exports;
    

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

    var TestContext = require('../lib/TestContext');
    describe("Unit TEST !", function () {
      var user = TestContext.user.base;
    
      before(function (done) {
        done();
      });
    
      describe(" do test ", function () {
    
        it(" do test ", function (done) {
          // 测试逻辑
          done(err);
        });
    
      });
    }
    

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

    还可以做点什么

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

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

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

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

  • 相关阅读:
    学习日记-- 动态性。动态编译,静态方法,包装类
    第一周学习所获———ORM,数据持久化,通过注解来实现数据持久化框架
    第一周学习所获--class类1(反射和类加载过程)
    各种命名规范
    用easyui,json,纯mvc实现一个系统的数据流动过程
    js+bootstrap实现分页页码
    Echarts简单案例
    bootstrap日期控件(双日期、清空等问题解决)
    三种方法实现调用Restful接口
    Spring MVC异常处理 和 重定向传递数据
  • 原文地址:https://www.cnblogs.com/myfjd/p/4671792.html
Copyright © 2020-2023  润新知