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

相關問題