2015-01-08 3 views
17

저는 Node를 처음 사용하고 knex와 bookshelf를 사용하는 프로젝트 작업을하고 있습니다. 내 코드를 테스트하는 데 문제가있는 유닛이 조금 있는데, 내가 뭘 잘못하고 있는지 잘 모르겠다.Bookshelf.js 및 knex.js를 이용한 단위 테스트

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

module.exports.VorcuProduct = VorcuProduct 

그리고는 DB에 존재하지 않는 경우 VorcuProduct을 저장하는 기능 :

는 기본적으로 I는 다음과 같습니다 모델 (VorcuProduct라고합니다)가 있습니다. 아주 간단합니다. 이 기능은 다음과 같습니다.

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

DB를 치지 않고 테스트하는 올바른 방법은 무엇입니까? 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를 사용하면 절반을 수행하는 데 걸리는 시간이 줄어 듭니다. –

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 

}); 
관련 문제