我有一个Javascript模块,它从另一个模块访问Promise对象,然后转换为它自己使用。我正在使用Bluebird库来确保所有的promise处理程序都是异步调用的。这对测试来说非常严重,特别是当内部承诺没有暴露时。
module.exports = (testedModule, app) ->
app.module('otherModule').somePromise.then transformValue
transformValue = (val) ->
return new Extra.TransformedValue(val)
在测试中,我正在嘲笑第一个承诺,所以我可以访问它。第二个承诺保留在模块内部,我不想仅仅为了测试而暴露它。请注意,我使用的是Mocha + Chai + Sinon。
beforeEach ->
@initModule = -> app.module('testedModule', testedModule) # prepare function to initialize tested module
@dfd = dfd = Promise.defer() # defer promise resolution to tests
app.module 'otherModule', (otherModule) ->
otherModule.somePromise = dfd.promise
@transformSpy = sinon.spy Extra, 'TransformedValue' # spy on the constructor function
@promiseTransform = dfd.promise.then =>
# this usually fails as the spy is called more then once due to async nature of the tests
@transformSpy.should.have.been.calledOnce
# promise gets resolved with the return value of the spy
# thus it should contain created instance of the TransformedValue
return @transformSpy.firstCall.returnValue
afterEach ->
@transformSpy.restore()
每次测试的一些准备工作。只需promiseTransform
即可在每个测试中单独使用dfd.resolve()
解析。但是transformSpy
本身附加到全局对象,该对象由所有测试共享(也许应该存根)。大多数测试看起来像这样:
it 'should test whatever...', ->
@init() # initialize module
# something else is tested here, doesn't matter
# note that @dfd is not resolved here, thus transformValue is not called yet
这样做很好,但接下来的测试实际上解决了dfd,这里的一切都变得混乱了。有时间谍不止一次解决或根本不解决。这是非常混乱的异步操作。
it 'should instantiate TransformedValue with correct argument', (done) ->
expected = {foo: "bar"}
@promiseTransform.then (transformed) =>
# test here that TransformedValue constructor has been called once
# ... it actually FAILS, because it was called twice already!
@transformSpy.withArgs(expected).should.have.been.calledOnce
transformed.should.be.an.instanceof Extra.TransformedValue
# somehow this resolves promise for previous test and
# it causes `transformValue` to be called back there
@dfd.resolve expected
@init()
我在这上花了2天时间,这已经让我疯了。测试应该是一个工具和要创建的实际代码。我可能错过了一些明显的解决方案。
您是否有任何一般(或具体)提示如何以更少的混淆和更多的控制和决定性来处理这个问题?目前我正在考虑将整个Promise存根以实际使其同步。但在我看来,它会使测试无效,因为工作流程可能与实际运行时不同。
答案 0 :(得分:1)
与间谍有什么关系?如果这是同步代码,你就不会使用间谍。如果一切都是同步的,你会怎么写测试?
为什么不把测试写成:
it('should instantiate TransformedValue with correct argument', function() {
var expected = {};
return transform(expected).then(function(val) {
assert.deepEqual(Extra.TransformedValue.value, expected)
assert(val instanceof Extra.TransformedValue);
});
});