【问题标题】:js callbacks to generator based codejs 回调到基于生成器的代码
【发布时间】:2016-08-25 12:46:31
【问题描述】:

我想测试我的服务器。 测试提供简单场景:

  1. 向服务器发送请求
  2. 等待回复
  3. 检查一下

我尝试使用mocha 进行测试,使用supertest 进行请求。

测试示例:

function request(url, query, cb) {
    var req = supertest(app.listen())
        .get(url)
        .query(query)
        .end(function(err, res){
            if (err) {throw (err);}
            cb(res);
        });
}

it('Check something after response', function *(done) {
    request(this.url, this.query, function(res) {/* some after response check here */});
});

现在我需要将我的回调样式代码重组为生成器样式代码。

我需要这样的东西:

it('Check something in response', function *(done) {
    var res = yield request(this.url, this.query);
    /* some after response check here */
});

很遗憾,我无法理解我需要在request() 中进行哪些更改

附:我不关注其他supertest 之类的具有适当风格的解决方案。我只是想了解如何用这个简单的例子来包装回调。

【问题讨论】:

    标签: javascript node.js callback mocha.js generator


    【解决方案1】:

    基本上,要将回调代码重写为生成器样式,您需要:

    • 一个使用yield someAsyncFunc(cb)的生成器函数
    • 通过调用此生成器创建的迭代器
    • 回调函数应该推进迭代器并设置它的返回值iter.next(value)
    • 最后,要开始整个过程​​,您应该在迭代器上调用一次next

    例子:

    console.info=function(x){document.write('<pre>'+JSON.stringify(x,0,3)+'</pre>')}
    //--
    
    
    // some async function with a callback
    function asyncFunc(done) {
        setTimeout(function() {
            done(Math.random())
        }, 500);
    }
    
    // generator
    function *gen() {
        var val;
        val = yield asyncFunc(advanceIter);
        console.info(val)
        val = yield asyncFunc(advanceIter);
        console.info(val)
    }
    
    // create an iterator
    iter = gen()
    
    // define a callback for the async function
    function advanceIter(value) {
        iter.next(value);
    }
    
    // get the whole machinery started
    iter.next()

    当然,在现实世界中,您会将生成器包装在一个负责所有内务管理的函数中。您的生成器将收到 advance 参数,它应该将其作为回调进一步传递给它使用的异步函数。

    console.info=function(x){document.write('<pre>'+JSON.stringify(x,0,3)+'</pre>')}
    //--
    
    function asyncFunc(done) {
        setTimeout(function() {
            done(Math.random())
        }, 500);
    }
    
    function run(gen) {
        var iter = gen(function(value) {
            iter.next(value)
        });
        iter.next()
    }
    
    run(function *(advance) {
        var val;
        val = yield asyncFunc(advance);
        console.info(val)
        val = yield asyncFunc(advance);
        console.info(val)
    });

    请注意,异步函数本身无需进行任何更改(在您的情况下为request)。

    一个测试框架的例子:

    // some async function we're going to test
    
    function asyncFunc(param, cb) {
        setTimeout(function () {
            cb(param + '-ok');
        }, 500);
    }
    
    // classic async test
    
    describe('async demo', function () {
        it('works', function (done) {
            asyncFunc('foobar', function (result) {
                expect(result).toBe('foobar-ok');
                done();
            })
        });
    });
    
    // generator test
    // note that asyncFunc itself remains unchanged
    
    function run(gen) {
        var iter = gen(function (value) {
            iter.next(value)
        });
        iter.next()
    }
    
    describe('yield demo', function () {
        it('works', function (done) {
            run(function *(advance) {
                var result = yield asyncFunc('barbaz', advance);
                expect(result).toBe('barbaz-ok');
                done();
            })
        });
    });
    
    // generator test 2
    // adding more automation
    
    function runGen(gen) {
        return function (done) {
            var iter = gen(function (value) {
                var r = iter.next(value);
                if (r.done)
                    done();
            });
            iter.next();
        }
    }
    
    describe('yield demo 2', function () {
        it('works', runGen(function *(advance) {
            var result = yield asyncFunc('quux', advance);
            expect(result).toBe('quux-ok');
        }));
    });
    

    【讨论】:

    • 除了最后一句,我都明白。如果我理解正确,我需要从参数中删除 cb 并将 cb(res) => done(res) 更改为我的异步函数。并编写类似于您的 run() 函数的内容。
    • @StepanLoginov:我添加了一个关于如何在测试中使用它的示例。
    • 非常感谢。我今天对这个话题感到绝望(mocha 在回调代码开始之前完成了测试)。所以我确实与你的第一个例子和“标志+睡眠”技术同步。现在我可以扔掉我所有的sleeps 并做好它。
    【解决方案2】:

    怎么样:

    function* request (url, query) {
        supertest(app.listen())
            .get(url)
            .query(query)
            .end((err, res) => {
                if (err) {
                    throw err;
                }
                yield res;
            });
    }
    
    it('Check something after response', done => {
        const res = request(this.url, this.query).next();
        expect(res).to.be.defined
    });
    

    【讨论】:

    • 我不确定“to.be.defined”部分是什么意思。但我不需要检查响应事实。我需要检查服务器在处理请求等时是否在数据库中创建了适当的实体...但是您的代码看起来真的很漂亮。
    • 对“to.be.defined”部分感到抱歉,这只是表达您想要测试的内容的“chai”方式。您可以使用 next() 作为您的函数来产生请求,然后继续检查您想要的服务器端
    猜你喜欢
    • 2016-02-01
    • 2013-11-19
    • 2013-04-21
    • 1970-01-01
    • 1970-01-01
    • 2012-08-20
    • 2011-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多