简体   繁体   English

使用Bookshelf.js和knex.js进行单元测试

[英]Unit testing with Bookshelf.js and knex.js

I'm relatively new to Node and am working on a project using knex and bookshelf. 我是Node的新手,正在使用knex和bookshelf开展项目。 I'm having a little bit of trouble unit testing my code and I'm not sure what I'm doing wrong. 我在测试代码时遇到了一些麻烦,我不确定我做错了什么。

Basically I have a model (called VorcuProduct) that looks like this: 基本上我有一个模型(称为VorcuProduct),如下所示:

var VorcuProduct = bs.Model.extend({
    tableName: 'vorcu_products'
});

module.exports.VorcuProduct = VorcuProduct

And a function that saves a VorcuProduct if it does not exist on the DB. 如果数据库上不存在VorcuProduct,则保存VorcuProduct的函数。 Quite simple. 非常简单。 The function doing this looks like this: 执行此操作的功能如下所示:

function subscribeToUpdates(productInformation, callback) {
  model.VorcuProduct
    .where({product_id: productInformation.product_id, store_id: productInformation.store_id})
    .fetch()
    .then(function(existing_model) {
        if (existing_model == undefined) {
            new model.VorcuProduct(productInformation)
                .save()
                .then(function(new_model) { callback(null, new_model)})
                .catch(callback);
        } else {
            callback(null, existing_model)
        }
    })
}

Which is the correct way to test this without hitting the DB? 在没有命中数据库的情况下测试这个的正确方法是什么? Do I need to mock fetch to return a model or undefined (depending on the test) and then do the same with save ? 我是否需要模拟fetch以返回模型或未定义(取决于测试),然后执行相同的save Should I use rewire for this? 我应该使用重新连线吗?

As you can see I'm a little bit lost, so any help will be appreciated. 你可以看到我有点失落,所以任何帮助将不胜感激。

Thanks! 谢谢!

I have been using in-memory Sqlite3 databases for automated testing with great success. 我一直在使用内存中的Sqlite3数据库进行自动化测试,取得了巨大的成功。 My tests take 10 to 15 minutes to run against MySQL, but only 30 seconds or so with an in-memory sqlite3 database. 我的测试需要10到15分钟才能对MySQL运行,但内存sqlite3数据库只需30秒左右。 Use :memory: for your connection string to utilize this technique. 使用:memory:用于连接字符串以利用此技术。

A note about unit tesing - This is not true unit testing, since we're still running a query against a database. 关于单元测试的注意事项 - 这不是真正的单元测试,因为我们仍然在对数据库运行查询。 This is technically integration testing, however it runs within a reasonable time period and if you have a query-heavy application (like mine) then this technique is going to prove more effective at catching bugs than unit testing anyway. 这是技术上的集成测试,但是它在合理的时间段内运行,如果你有一个查询繁重的应用程序(比如我的),那么这种技术在捕获错误方面比单元测试更有效。

Gotchas - Knex/Bookshelf initializes the connection at the start of the application, which means that you keep the context between tests. 问题 - Knex / Bookshelf在应用程序启动时初始化连接,这意味着您在测试之间保持上下文。 I would recommend writing a schema create/destroy script so that you and build and destroy the tables for each test. 我建议编写一个模式创建/销毁脚本,以便您为每个测试构建和销毁表。 Also, Sqlite3 is less sensitive about foreign key constraints than MySQL or PostgreSQL, so make sure you run your app against one of those every now and then to ensure that your constraints will work properly. 此外,Sqlite3对外键约束的敏感度低于MySQL或PostgreSQL,因此请确保您不时地针对其中一个运行应用程序,以确保您的约束能够正常运行。

This is actually a great question which brings up both the value and limitations of unit testing. 这实际上是一个很好的问题,它带来了单元测试的价值和局限性。

In this particular case the non-stubbed logic is pretty simple -- just a simple if block, so it's arguable whether it's this is worth the unit testing effort, so the accepted answer is a good one and points out the value of small scale integration testing. 在这个特殊情况下,非存根逻辑非常简单 - 只是一个简单的if块,所以它是否值得单元测试工作是有争议的,所以接受的答案是好的,并指出小规模集成的价值测试。

On the other hand the exercise of doing unit testing is still valuable in that it points out opportunities for code improvements. 另一方面,进行单元测试的工作仍然很有价值,因为它指出了代码改进的机会。 In general if the tests are too complicated, the underlying code can probably use some refactoring. 通常,如果测试太复杂,底层代码可能会使用一些重构。 In this case a doesProductExist function can likely be refactored out. 在这种情况下,可能会重构doesProductExist函数。 Returning the promises from knex/bookshelf instead of converting to callbacks would also be a helpful simplification. 从knex / bookshelf返回承诺而不是转换为回调也是一种有用的简化。

But for comparison here's my take on what true unit-testing of the existing code would look like: 但是为了比较,这是我对现有代码的真正单元测试的看法:

var rewire = require('rewire');
var sinon = require('sinon');
var expect = require('chai').expect;
var Promise = require('bluebird');
var subscribeToUpdatesModule = rewire('./service/subscribe_to_updates_module');

var subscribeToUpdates = subscribeToUpdatesModule.__get__(subscribeToUpdates);

describe('subscribeToUpdates', function () {
  before(function () {
    var self = this;
    this.sandbox = sinon.sandbox.create();
    var VorcuProduct = subscribeToUpdatesModule.__get__('model').VorcuProduct;

    this.saveStub = this.sandbox.stub(VorcuProduct.prototype, 'save');
    this.saveStub.returns(this.saveResultPromise);

    this.fetchStub = this.sandbox.stub()
    this.fetchStub.returns(this.fetchResultPromise);

    this.sandbox.stub(VorcuProduct, 'where', function () {
      return { fetch: self.fetchStub };
    })

  });

  afterEach(function () {
    this.sandbox.restore();
  });

  it('calls save when fetch of existing_model succeeds', function (done) {
    var self = this;
    this.fetchResultPromise = Promise.resolve('valid result');
    this.saveResultPromise = Promise.resolve('save result');
    var callback = function (err, result) {
      expect(err).to.be.null;
      expect(self.saveStub).to.be.called;
      expect(result).to.equal('save result');
      done();
    };
    subscribeToUpdates({}, callback);
  });

  // ... more it(...) blocks

});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM