Unit testing Firebase云功能的单元测试:what';这是“什么?”;“正确的道路”;使用sinon.js测试/模拟“事务”

Unit testing Firebase云功能的单元测试:what';这是“什么?”;“正确的道路”;使用sinon.js测试/模拟“事务”,unit-testing,firebase,sinon,google-cloud-functions,sinon-chai,Unit Testing,Firebase,Sinon,Google Cloud Functions,Sinon Chai,伙计,这个firebase单元测试真是让我大吃一惊 我已经仔细阅读了他们提供的示例,并对一些更基本的Firebase功能进行了单元测试,但是我一直遇到一些问题,我不确定如何验证传递给refs的transactionUpdated函数是否正确地更新了当前的对象 我的挣扎可能最好地用他们和我为之编写单元测试的拙劣尝试来说明 假设我要进行单元测试的函数执行以下操作(直接从上面的链接获取): 单元测试代码: const chai = require('chai'); const chaiAsPromis

伙计,这个firebase单元测试真是让我大吃一惊

我已经仔细阅读了他们提供的示例,并对一些更基本的Firebase功能进行了单元测试,但是我一直遇到一些问题,我不确定如何验证传递给refs
transactionUpdated
函数是否正确地更新了
当前的
对象

我的挣扎可能最好地用他们和我为之编写单元测试的拙劣尝试来说明

假设我要进行单元测试的函数执行以下操作(直接从上面的链接获取):

单元测试代码:

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
并将其变为
expectedValue
,然后允许我观察该更改并断言它发生了


这可能是一个非常简单的问题,有一个明显的解决方案,但我对测试JS代码非常陌生,在这里所有的东西都必须被存根,所以这是一个学习曲线。。。非常感谢您的帮助。

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

我建议大家看看我们刚刚发表的文章。在那个示例中,我们使用TypeScript,但这也可以在JavaScript中使用

src
目录中,您会注意到我们已将逻辑分为三个文件:
index.ts
具有条目逻辑,
表示.ts
具有我们的主要业务逻辑,
db.ts
是Firebase实时数据库周围的一个薄抽象层。我们只进行单元测试
说.ts
;我们故意使
index.ts
db.ts
非常简单

spec
目录中,我们有单元测试;看看
index.spec.ts
。您正在寻找的技巧:我们
mock要求
模拟整个
src/db.ts
文件,并用
spec/fake db.ts
替换它。我们现在将执行的操作存储在内存中,单元测试可以在内存中检查它们是否正确,而不是写入真实的数据库。一个具体的例子是我们的
score
字段,它是。通过数据库,我们的单元测试可以检查是否正确完成了


我希望这能帮助你做测试

太好了!感谢您提供的资源和回复。除了测试,我对云功能产品印象深刻,它非常强大——文档通常也很好,只是测试部门缺乏。祝你和你的团队工作愉快。谢谢蒂姆!如果您遇到更多问题,请告诉我们。我们渴望让一切变得精彩。:)@Robert JanHuijsman我也对firebase印象深刻,我已经花了三天的时间在它上面,因为我认为这将是我和其他很多人后端的未来。我希望你有一张路线图。所以我们可以知道你在做什么。很高兴听到!注意到您的路线图请求!同时,我建议注册我们的Alpha项目——这将让你一窥我们正在开发的许多很酷的技术:firebase.google.com/Alpha
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)
    })
  })
})