javascript - 如何在单元测试中处理嵌套异步操作

标签 javascript unit-testing asynchronous promise

我有一个 Javascript 模块,它从另一个模块访问 Promise 对象,然后将其转换为自己使用。我正在使用Bluebird确保所有 Promise 处理程序都是异步调用的库。这对于测试来说是一个很大的问题,特别是当内部 promise 没有公开时。

module.exports = (testedModule, app) ->
    app.module('otherModule').somePromise.then transformValue

transformValue = (val) ->
    return new Extra.TransformedValue(val)

在测试中,我 mock 第一个 promise ,所以我可以访问它。第二个 promise 保留在模块内部,我不想仅仅为了测试而公开它。请注意,我使用的是 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 本身附加到所有测试共享的全局对象(也许也应该被 stub )。大多数测试如下所示:

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 的测试,这里一切都变得困惑。有时 spy 会被多次解决或根本没有解决。这是非常令人困惑的异步操作竞赛。

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

我在这上面花了大约两天的时间,这已经让我抓狂了。测试应该是一个工具和实际的代码来创建。我可能错过了一些明显的解决方案。

您是否有任何一般(或具体)提示如何以更少的困惑和更多的控制和确定性来处理这个问题?目前我正在考虑对整个 Promise 进行 stub 以使其真正同步。但在我看来,这有点使测试无效,因为工作流程可能与实际运行中的工作流程不同。

最佳答案

spy 怎么了?如果这是同步代码,您就不会使用 spy 。如果一切都是同步的,您将如何编写测试?

为什么不将测试编写为:

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

关于javascript - 如何在单元测试中处理嵌套异步操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23593674/

相关文章:

multithreading - asp.net mvc 3应用程序需要写一行,然后等待该行被更新,然后再更新UI

javascript - 通过 HTML 表单字段名称获取具有内部对象的 JSON 对象的值,无需 eval

javascript - 关于如何向用户显示给定文本的建议标签的 UI 建议?

javascript - 为什么我的 jest.mock 中的 Promisereject() 会转到 then() 而不是 catch()?

unit-testing - 使用 Moq 模拟方法的问题

php - cURL 有时会为有效的 URL 返回空白字符串

javascript - 重新设置对 anchor 标记的关注

javascript - 简单的窗口调整大小 react

unit-testing - TeamCity VSTest 2015

c# - 如何序列化异步/等待?