2017-09-14 5 views
0

現在、Google Cloud機能を調査中で、いくつかの基本的なテスト機能がtypescriptで書かれています。ジャスミンとTypescriptを使用したFirebaseのクラウド機能のテスト

機能は期待通りに機能し、今ではジャスミンを使用して単体テストを作成しようとしています。 (私はChai/sinonをドキュメントごとに使用していません。私のプロジェクトの残りの部分はジャスミンを使用しています)。

私は2つの問題 1を持っている)テストが

throw new Error('Firebase config variables are not available. ' + ^ Error: Firebase config variables are not available. Please use the latest version of the Firebase CLI to deploy this function

2により、このエラーには実行されません)テストを実行したのを考えると、私は予想通りの応答があることをテストするかどうかはわかりません。

インデックスファイル

import * as functions from 'firebase-functions' 

import { helloWorldHandler } from './functions/hello-world'; 

export let helloWorld = functions.https.onRequest((req, res) => { 
    helloWorldHandler(req, res); 
}); 

テスト中のファイル

export let helloWorldHandler = (request, response) => { 
    response.send("Hello from Firebase Cloud!"); 
} 

スペック

import {} from 'jasmine'; 
import * as functions from 'firebase-functions' 
import { helloWorldHandler } from './hello-world'; 
import * as endpoints from '../index'; 

describe('Cloud Functions : Hello World',() => { 

    let configStub = { 
     firebase: { 
      databaseURL: "https://myProject.firebaseio.com", 
      storageBucket: "myProject.appspot.com", 
     } 
    }; 

    it('should return correct message',() => { 

     let spy = spyOn(functions, 'config').and.returnValue(configStub); 

     const expected = 'Hello from Firebase Cloud!'; 
     // A fake request and response objects 
     const req : any = {}; 
     const res : any = { }; 

     endpoints.helloWorld(req, res); 

     //here test response from helloWorld is as expected 

     }); 


}); 
+0

エミュレータから[機能をローカルで実行](https://firebase.google.com/docs/functions/local-emulator)できるようになりました。これはテストのための助けになります。 – Kato

答えて

3

あなたはユニットテストを書いている場合は、サードパーティのAPIをテストする必要はありません。したがって、目標はコードロジックを分離してテストすることです。エンドツーエンドのテストは、統合の回帰テストに最適です。

ここでの最初のステップは、firebase-functionsなどのツールと、データベースSDKを画像から削除することです(これは妥当な限り)。私はそうのような機能ロジックから私のLIBSを分離することによって、これを達成:

// functions/lib/http.js 
exports.httpFunction = (req, res) => { 
    res.send(`Hello ${req.data.foo}`); 
}; 

// functions/index.js 
const http = require('lib/http'); 
const functions = require('firebase-functions'); 

// we have decoupled the Functions invocation from the method 
// so the method can be tested without including the functions lib! 
functions.https.onRequest(http.httpFunction); 

今私はうまく私はユニットテストを経てテストすることができロジックを単離しました。私は自分のメソッドに渡される引数を模倣し、サードパーティのAPIをそのピクチャから削除します。複雑さの多くは、サードパーティのLIBSのテストであり

// spec/lib/http.spec.js 
const http = require('../functions/lib/http'); 

describe('functions/lib/http',() => { 
    expect('send to be called with "hello world"',() => { 
     // first thing to do is mock req and res objects 
     const req = {data: {foo: 'world'}}; 
     const res = {send: (s) => {}); 

     // now let's monitor res.send to make sure it gets called 
     spyOn(res, 'send').and.callThrough(); 

     // now run it 
     http.httpFunction(req, res); 

     // new test it 
     expect(res.send).toHaveBeenCalledWith("Hello world"); 
    }); 
}); 

:だからここ

は、ジャスミンの私のユニットテストがどのように見えるかです。 TDD/BDDの原則を早期かつ抽象的な第三者のライブラリを容易に嘲笑できるサービスに適用するための最良の答え。私は、この例をテストするには

// functions/lib/http.js 
const functions = require('firebase-functions'); 
const admin = require('firebase-admin'); 
const env = require('./env'); 
const serviceAccount = require(env.serviceAccountPath); 

admin.initializeApp({ 
    credential: admin.credential.cert(serviceAccount), 
    databaseURL: `https://${env.dbUrl}.firebaseio.com` 
}); 

exports.httpFunction = (req, res) => { 
    let path = null; 
    let data = null; 

    // this is what I really want to test--my logic! 
    if(req.query.foo) { 
     path = 'foo'; 
     data = 1; 
    } 

    // but there's this third library party coupling :(
    if(path !== null) { 
    let ref = admin.database.ref().child(path); 
    return ref.set(data) 
     .then(() => res.send('done')) 
     .catch(e => res.status(500).send(e)); 
    } 
    else { 
     res.status(500).send('invalid query'); 
    } 
}; 

:私は私の関数内Firebase管理者と対話した場合

例えば、私は簡単にと競合するサードパーティの依存関係がたくさんある方法で終わる可能性がありFirebase Admin SDKと同様に、関数をインクルードして初期化する必要があります。そうしなければ、これらのサービスを模擬する方法を見つけなければなりません。これらはすべてかなり大きな仕事のように見えます。代わりに、私はデータストアの抽象化を持っており、それを利用することができます:私は今、機能プロセスから私のロジックを抽象化の第一原理に追加した場合

// An interface for the DataStore abstraction 
// This is where my Firebase logic would go, neatly packaged 
// and decoupled 
class DataStore { 
    set: (path, data) => { 
     // This is the home for admin.database.ref(path).set(data); 
    } 
} 

// An interface for the HTTPS abstraction 
class ResponseHandler { 
    success: (message) => { /* res.send(message); */ } 
    fail: (error) => { /* res.status(500).send(error); */ } 
} 

、その後、私は次のようなレイアウトを持っている:

// functions/lib/http.js 
exports.httpFunction = (query, responseHandler, dataStore) => { 
    if(query.foo) { 
     return dataStore.set('foo', 1) 
     .then(() => responseHandler.success()) 
     .catch(e => responseHandler.fail(e)); 
    } 
    else { 
     responseHandler.fail('invalid query'); 
    } 
}; 
私ははるかにエレガントですユニットテストの書き込みを許可

// spec/lib/http 
describe('functions/lib/http',() => { 
    expect('is successful if "foo" parameter is passed',() => { 
     // first thing to do is mock req and res objects 
     const query = {foo: 'bar'}; 
     const responseHandler = {success:() => {}, fail:() => {}); 
     const dataStore = {set:() => {return Promise.resolve()}}; 

     // now let's monitor the results 
     spyOn(responseHandler, 'success'); 

     // now run it 
     http.httpFunction(query, responseHandler, dataStore); 

     // new test it 
     expect(res.success).toHaveBeenCalled(); 
    }); 
}); 

そして、私のコードの残りの部分はどちらか半分悪くないです。

// functions/lib/firebase.datastore.js 
// A centralized place for our third party lib! 
// Less mocking and e2e testing! 
const functions = require('firebase-functions'); 
const admin = require('firebase-admin'); 
const serviceAccount = require(env.serviceAccountPath); 

admin.initializeApp({ 
    credential: admin.credential.cert(serviceAccount), 
    databaseURL: `https://${env.dbUrl}.firebaseio.com` 
}); 

exports.set = (path, data) => { 
    return admin.database.ref(path).set(data); 
}; 

// functions/index.js 
const functions = require('firebase-functions'); 
const dataStore = require('./lib/firebase.datastore'); 
const ResponseHandler = require('./lib/express.responseHandler'); 
const env = require('./env'); 
const http = require('./lib/http'); 

dataStore.initialize(env); 

exports.httpFunction = (req, res) => { 
    const handler = new ResponseHandler(res); 
    return http.httpFunction(req.query, handler, dataStore); 
}; 

私はBDDの考え方が優れていることは言うまでもなく、フェーズ2のすべてのスコープクリープについて知ったときに、私のプロジェクトのコンポーネントをモジュラー形式で素敵に分離しました。 :)

関連する問題