Man, this firebase unit testing is really kicking my butt.
I've gone through the documentation and read through the examples that they provide, and have gotten some of my more basic Firebase functions unit tested, but I keep running into problems where I'm not sure how to verify that the transactionUpdated
function passed along to the refs .transaction
is correctly updating the current
object.
My struggle is probably best illustrated with their child-count
sample code and a poor attempt I made at writing a unit test for it.
Let's say my function that I want to unit test does the following (taken straight from that above link):
// 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.');
});
});
Unit Test Code:
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)
})
})
})
I annotated the source code above with my question, but I'll reiterate it here.
How can I verify that the transactionUpdate
callback, passed to the transaction stub, will take my startingValue
and mutate it to expectedValue
and then allow me to observe that change and assert that it happened.
This is probably a very simple problem with an obvious solution, but I'm very new to testing JS code where everything has to be stubbed, so it's a bit of a learning curve... Any help is appreciated.