对Firebase的云功能进行单元测试:"正确的方式"使用sinon.js测试/模拟`transaction`s

时间:2017-05-18 18:39:57

标签: unit-testing firebase sinon google-cloud-functions sinon-chai

男人,这个firebase单元测试真的踢我的屁股。

我已经浏览了the documentation并阅读了他们提供的示例,并且已经对我的一些基本的Firebase功能单元进行了测试,但是我一直遇到问题我不会确保如何验证传递给引用transactionUpdated的{​​{1}}函数是否正确更新.transaction对象。

我的斗争可能最好用他们的child-count sample code来说明,并且我在编写单元测试时做的很差。

让我们说我想要进行单元测试的功能如下(直接从上面的链接中获取):

current

单元测试代码:

// 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.');
      });
    });

我用我的问题注释了上面的源代码,但我在这里重复一遍。

如何验证传递给事务存根的transactionUpdate callback是否会接受我的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) }) }) }) 并将其变为startingValue然后允许我观察该更改并断言它发生了。

这可能是一个非常简单的问题,有一个明显的解决方案,但我对测试JS代码非常新,其中所有东西都必须存根,所以它有点学习曲线...任何帮助非常感谢。

1 个答案:

答案 0 :(得分:9)

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

我建议您查看我们刚刚发布的this Cloud Functions demo。在该示例中,我们使用TypeScript,但这也是在JavaScript中工作的。

src目录中,您会注意到我们已将逻辑拆分为三个文件:index.ts具有输入逻辑,saythat.ts包含我们的主要业务-logic,db.ts是Firebase实时数据库的精简抽象层。我们仅对saythat.ts进行单元测试;我们故意让index.tsdb.ts非常简单。

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

我希望这可以帮助您进行测试!