2015-01-08 4 views
17

私はNodeには比較的新しいので、knexとbookshelfを使ってプロジェクトを進めています。私は自分のコードをテストするのに少し問題があります。私は何が間違っているのか分かりません。Bookshelf.jsとknex.jsを使用したユニットテスト

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

module.exports.VorcuProduct = VorcuProduct 

そして、それはDBに存在しない場合VorcuProductを節約機能:

は基本的に私はこのようになりますモデル(VorcuProductと呼ばれる)を持っています。非常に簡単です。 DBを押すことなくこれをテストするための正しい方法です

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) 
     } 
    }) 
} 

:これを行う機能は、次のようになりますか? fetchをモデルに返すか、テストによっては未定義にする必要がありますか?saveと同じですか?私はこれに再配線を使うべきですか?

ご覧のとおり、私は少し失われているので、どんな助けにも感謝します。

ありがとうございます!

答えて

16

私はin-memory Sqlite3 databasesを自動テストに大いに活用しています。私のテストは、MySQLに対して実行するには10〜15分かかりますが、メモリ内のsqlite3データベースでは30秒程度です。このテクニックを利用するには、接続文字列に:memory:を使用してください。

単位テーピングについての注意 - これはデータベースに対してクエリを実行しているため、ユニットテストではありません。これは技術的には統合テストですが、合理的な時間内に実行されます。クエリーが重いアプリケーション(鉱山のようなもの)があれば、このテクニックは単体テストよりもバグを捕らえることでより効果的です。

Gotchas - Knex/Bookshelfは、アプリケーションの開始時に接続を初期化します。つまり、テスト間でコンテキストを保持します。私はスキーマ作成/破壊スクリプトを書くことをお勧めします。そうすれば、各テストのためにテーブルを構築して破壊することができます。また、Sqlite3はMySQLやPostgreSQLよりも外部キーの制約に敏感ではありませんので、あなたのアプリケーションをいつでも実行して、制約が正しく機能するようにしてください。

+0

ありがとう:

しかし、比較のため

は、ここでは、既存のコードの真のユニットテストはどのように見えるかが私の感想です。好奇心の中で、いくつのテストを実行していますか?また、セットアップにはかなりの量のシードデータをロードする必要がありますか? – thebearingedge

+1

@thebearingedge私は合計約1,000のキュウリのステップで約70のシナリオを実行します。私はそれぞれのシナリオで60台のテーブルをセットアップして解体しました。メモリ内のsqliteでは、それを実行するのに0.5秒もかかりません。 –

3

これは、実際にはユニットテストの価値と限界の両方をもたらす大きな問題です。

ブロックされていないロジックは非常に単純です。単純なifブロックなので、ユニットテストの価値があるかどうかは分かりません。小規模な統合テストの

一方、単体テストを行うエクササイズは、コード改善の機会を指摘する点で依然として貴重です。一般に、テストが複雑すぎると、基礎となるコードでおそらくリファクタリングを使用する可能性があります。この場合、doesProductExist関数がリファクタリングされる可能性があります。コールバックに変換するのではなく、knex/bookshelfからの約束を返すことも有益な簡素化になります。あなたの経験を共有するための

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 

}); 
関連する問題