用茉莉和打字稿测试云功能



我目前正在研究Google Cloud功能,并在打字稿中编写了一些基本的测试功能。

这些功能按预期工作,我现在正在尝试使用茉莉花创建单位测试。(我没有根据文档使用柴/辛农

我有两个问题1)由于此错误

,该测试不进行

投掷新错误('Firebase配置变量不可用。' ^ 错误:firebase配置变量不可用。请使用 Firebase CLI的最新版本要部署此功能

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

});

如果您正在编写单元测试,则不想测试第三方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。

所以这是我的单位测试在茉莉花中的样子:

// 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原则提前和抽象的第三方LIB轻松嘲笑。

例如,如果我在功能中与firebase管理员进行互动,我很容易最终获得具有许多第三方依赖性的方法:

// 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,否则我必须找到一种嘲笑这些服务的方法。所有这些看起来都很大。相反,我可以使用数据存储抽象并利用:

// 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中找到所有范围蠕变时,这将变得很好。/p>

相关内容

  • 没有找到相关文章

最新更新