2017-09-14 2 views
0

현재 Google 클라우드 기능을 조사 중이며 타이프 스크립트로 작성된 몇 가지 기본 테스트 기능이 있습니다.재스민과 대소 문자가있는 Firebase 용 클라우드 기능 테스트

예상대로 기능이 작동하며 이제 재스민을 사용하여 단위 테스트를 만들려고합니다. (내 프로젝트의 나머지 부분은 재스민을 사용하므로 문서별로 Chai/sinon을 사용하지 않습니다.)

내가 인해 테스트를 실행할 않았다 감안할 때이 오류

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)에 두 가지 문제 1) 테스트가 실행되지 않습니다가, 나는 응답이 예상대로하는지 테스트하는 방법을 모르겠어요. 테스트

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를 테스트하지 않으려는 아래

색인 파일

import * as functions from 'firebase-functions' 

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

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

파일. 따라서 목표는 코드 논리를 분리하고이를 테스트해야합니다. 엔드 투 엔드 테스트는 통합을 회귀 테스트하는 데 가장 적합합니다.

첫 번째 단계는 여기 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); 

지금은 잘 격리 한 논리를. 그림에서 제 3 자 API를 제거하고 내 메소드에 전달 될 인수를 모의합니다.

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

타사 libs와 테스트와 복잡성이 많이 있습니다

그래서 여기처럼 재스민 내 단위 테스트를 볼거야. 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 Admin SDK뿐만 아니라 함수를 포함하고 초기화해야합니다. 그렇지 않으면 해당 서비스를 모방 할 방법을 찾아야합니다. 이 모든 것은 꽤 큰 일처럼 보입니다.대신에, 나는 데이터 저장소 추상화를 가지고 있음을 이용할 수있다 :

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

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

내가 지금 함수 과정에서 내 로직을 추상화의 첫 번째 원칙에 추가하면, 나는 다음과 같은 레이아웃을 가지고 내가 훨씬 더 우아 단위 테스트 작성 허용

:

// 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 단계에서 모든 범위 크립에 대해 알게 될 때 모듈 식으로 프로젝트 구성 요소를 멋지게 고립 시켰습니다. :)

관련 문제