당신이 단위 테스트를 작성하는 경우, 당신은 타사 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 단계에서 모든 범위 크립에 대해 알게 될 때 모듈 식으로 프로젝트 구성 요소를 멋지게 고립 시켰습니다. :)
테스트 용 도움말 인 에뮬레이터를 통해 이제 [로컬 함수] (https://firebase.google.com/docs/functions/local-emulator)를 실행할 수 있습니다. – Kato