2

这个firebase单元测试正在踢我的屁股。Firebase的单元测试云功能:用sinon.js测试/模拟“交易”的“正确方法”是什么

我已经通过the documentation并阅读了他们提供的示例,并且已经获得了一些经过测试的更基本的Firebase功能单元,但是我不知道如何验证如何验证transactionUpdated传递给参考号.transaction的函数正在更新current对象。

我的努力可能是最好的说明,他们的child-count sample code和我在编写单元测试时做的很差。

比方说,我的功能,我想单元测试包括以下内容(直接从上面的链接所):

// count.js 
exports.countlikechange = functions.database.ref('/posts/{postid}/likes/{likeid}').onWrite(event => { 
     const collectionRef = event.data.ref.parent; 
     const countRef = collectionRef.parent.child('likes_count'); 

     // ANNOTATION: I want to verify the `current` value is incremented 
     return countRef.transaction(current => { 
     if (event.data.exists() && !event.data.previous.exists()) { 
      return (current || 0) + 1; 
     } 
     else if (!event.data.exists() && event.data.previous.exists()) { 
      return (current || 0) - 1; 
     } 
     }).then(() => { 
     console.log('Counter updated.'); 
     }); 
    }); 

单元测试代码:

const chai = require('chai'); 
const chaiAsPromised = require("chai-as-promised"); 
chai.use(chaiAsPromised); 
const assert = chai.assert; 
const sinon = require('sinon'); 

describe('Cloud Functions',() => { 
    let myFunctions, functions; 

    before(() => { 
    functions = require('firebase-functions'); 
    myFunctions = require('../count.js'); 
    }); 

    describe('countlikechange',() => { 
    it('should increase /posts/{postid}/likes/likes_count',() => { 
     const event = { 
     // DeltaSnapshot(app: firebase.app.App, adminApp: firebase.app.App, data: any, delta: any, path?: string); 
     data: new functions.database.DeltaSnapshot(null, null, null, true) 
     } 

     const startingValue = 11 
     const expectedValue = 12 

     // Below code is misunderstood piece. How do I pass along `startingValue` to the callback param of transaction 
     // in the `countlikechange` function, and spy on the return value to assert that it is equal to `expectedValue`? 
     // `yield` is almost definitely not the right thing to do, but I'm not quite sure where to go. 
     // How can I go about "spying" on the result of a stub, 
     // since the stub replaces the original function? 
     // I suspect that `sinon.spy()` has something to do with the answer, but when I try to pass along `sinon.spy()` as the yields arg, i get errors and the `spy.firstCall` is always null. 
     const transactionStub = sinon.stub().yields(startingValue).returns(Promise.resolve(true)) 

     const childStub = sinon.stub().withArgs('likes_count').returns({ 
     transaction: transactionStub 
     }) 
     const refStub = sinon.stub().returns({ parent: { child: childStub }}) 

     Object.defineProperty(event.data, 'ref', { get: refStub }) 

     assert.eventually.equals(myFunctions.countlikechange(event), true) 
    }) 
    }) 
}) 

我注释上面的源代码与我的问题,但我会在这里重申。

我如何可以验证该transactionUpdate callback,传递给交易存根,将我的startingValue,它发生变异,expectedValue,然后让我观察到的变化,并断言,它发生了。

这可能是一个非常简单的问题,但有一个明显的解决方案,但我对测试JS代码非常新,因为它的一切都需要被截断,所以它有点像学习曲线......任何帮助都会被赞赏。

回答

4

我同意在Firebase生态系统中进行单元测试并不像我们希望的那样容易。团队意识到这一点,我们正在努力让事情变得更好!幸运的是,有现在有一些很好的方法!

我建议看看我们刚发布的this Cloud Functions demo。在这个例子中,我们使用了TypeScript,但是这也可以在JavaScript中使用。

src目录,你会发现我们已经打出了逻辑分为三个文件:index.ts具有项的逻辑,saythat.ts有我们的主要业务逻辑,并db.ts围绕火力地堡实时数据库薄薄的抽象层。我们只进行单元测试saythat.ts;我们故意保留index.tsdb.ts真的很简单。

spec目录中我们有单元测试;看看index.spec.ts。您正在寻找的技巧:我们usemock-require来嘲笑整个src/db.ts文件并将其替换为spec/fake-db.ts。我们现在不是写入真实数据库,而是将我们执行的操作存储在内存中,我们的单元测试可以检查它们看起来是否正确。一个具体的例子是我们的score字段,它是updated in a transaction。通过mocking的数据库,我们的单元测试检查是否正确完成是a single line of code

我希望能帮助你做你的测试!

+0

太棒了!感谢资源和回应。除了测试之外,我对云功能产品印象深刻,功能非常强大 - 文档通常也很好,仅缺少测试部门。对你和你的团队来说很棒的工作。 – tim

+0

谢谢蒂姆!如果您遇到更多问题,请告诉我们。我们渴望让所有的事情都变得可怕。 :) –

+0

@ Robert-JanHuijsman我也对Firebase留下了深刻的印象,我已经投入了最后3天,因为我认为这将成为我和其他人的后端的未来。我希望你有一件事是路线图。所以我们可以知道你在做什么。 – Ced

相关问题