How do you unit test a firebase function wrapped with express? - firebase

With firebase functions, you can utilize express to achieve nice functionality, like middleware etc. I have used this example to get inspiration of how to write https firebase functions, powered by express.
However, my issue is that the official firebase documentation on how to do unit testing, does not include a https-express example.
So my question is how to unit test the following function (typescript)?:
// omitted init. of functions
import * as express from 'express';
const cors = require('cors')({origin: true});
const app = express();
app.use(cors);
// The function to test
app.get('helloWorld, (req, res) => {
res.send('hello world');
return 'success';
});
exports.app = functions.https.onRequest(app);

This works with Jest
import supertest from 'supertest'
import test from 'firebase-functions-test'
import sinon from 'sinon'
import admin from 'firebase-admin'
let undertest, adminInitStub, request
const functionsTest = test()
beforeAll(() => {
adminInitStub = sinon.stub(admin, 'initializeApp')
undertest = require('../index')
// inject with the exports.app methode from the index.js
request = supertest(undertest.app)
})
afterAll(() => {
adminInitStub.restore()
functionsTest.cleanup()
})
it('get app', async () => {
let actual = await request.get('/')
let { ok, status, body } = actual
expect(ok).toBe(true)
expect(status).toBeGreaterThanOrEqual(200)
expect(body).toBeDefined()
})

You can use supertest paired with the guide from Firebase. Below is a very basic example of testing your app, however, you can make it more complex/better by integrating mocha.
import * as admin from 'firebase-admin'
import * as testFn from 'firebase-functions-test'
import * as sinon from 'sinon'
import * as request from 'supertest'
const test = testFn()
import * as myFunctions from './get-tested' // relative path to functions code
const adminInitStub = sinon.stub(admin, 'initializeApp')
request(myFunctions.app)
.get('/helloWorld')
.expect('hello world')
.expect(200)
.end((err, res) => {
if (err) {
throw err
}
})

Would something like mock-express work for you? It should allow you to test paths without actually forcing you to make the express server.
https://www.npmjs.com/package/mock-express

I've gotten this to work using firebase-functions-test and node-mocks-http.
I have this utility class FunctionCaller.js :
'use strict';
var httpMocks = require('node-mocks-http');
var eventEmitter = require('events').EventEmitter;
const FunctionCaller = class {
constructor(aYourFunctionsIndex) {
this.functions_index = aYourFunctionsIndex;
}
async postFunction(aFunctionName,aBody,aHeaders,aCookies) {
let url = (aFunctionName[0]=='/') ? aFunctionName : `/${aFunctionName}`;
let options = {
method: 'POST',
url: url,
body: aBody
};
if (aHeaders)
options.headers = aHeaders;
if (aCookies) {
options.cookies = {};
for (let k in aCookies) {
let v = aCookies[k];
if (typeof(v)=='string') {
options.cookies[k] = {value: v};
} else if (typeof(v)=='object') {
options.cookies[k] = v;
}
}
}
var request = httpMocks.createRequest(options);
var response = httpMocks.createResponse({eventEmitter: eventEmitter});
var me = this;
await new Promise(function(resolve){
response.on('end', resolve);
if (me.functions_index[aFunctionName])
me.functions_index[aFunctionName](request, response);
else
me.functions_index.app(request, response);
});
return response;
}
async postObject(aFunctionName,aBody,aHeaders,aCookies) {
let response = await this.postFunction(aFunctionName,aBody,aHeaders,aCookies);
return JSON.parse(response._getData());
}
async getFunction(aFunctionName,aParams,aHeaders,aCookies) {
let url = (aFunctionName[0]=='/') ? aFunctionName : `/${aFunctionName}`;
let options = {
method: 'GET',
url: url,
query: aParams // guessing here
};
if (aHeaders)
options.headers = aHeaders;
if (aCookies) {
options.cookies = {};
for (let k in aCookies) {
let v = aCookies[k];
if (typeof(v)=='string') {
options.cookies[k] = {value: v};
} else if (typeof(v)=='object') {
options.cookies[k] = v;
}
}
}
var request = httpMocks.createRequest(options);
var response = httpMocks.createResponse({eventEmitter: eventEmitter});
var me = this;
await new Promise(function(resolve){
response.on('end', resolve);
if (me.functions_index[aFunctionName])
me.functions_index[aFunctionName](request, response);
else
me.functions_index.app(request, response);
});
return response;
}
async getObject(aFunctionName,aParams,aHeaders,aCookies) {
let response = await this.getFunction(aFunctionName,aParams,aHeaders,aCookies);
return JSON.parse(response._getData());
}
};
module.exports = FunctionCaller;
and my app is mounted as app :
exports.app = functions.https.onRequest(expressApp);
and my firebase.json contains :
"rewrites": [
:
:
:
{
"source": "/path/to/function", "function": "app"
}
]
In my test file at the top I do :
const FunctionCaller = require('../FunctionCaller');
let fire_functions = require('../index');
const fnCaller = new FunctionCaller(fire_functions);
and then in the test I do :
let response = await fnCaller.postFunction('/path/to/function',anObject);
and it calls my function with anObject as request.body and returns the response object.
I'm using node 8 on Firebase to get async/await etc.

For local & no-network unit tests, you could refactor the app.get("helloWorld", ...) callback into a separate function and call it with mock objects.
A general approach would be something like this:
main.js:
// in the Firebase code:
export function helloWorld(req, res) { res.send(200); }
app.get('helloWorld', helloWorld);
main.spec.js: using jasmine & sinon
// in the test:
import { helloWorld } from './main.js';
import sinon from 'sinon';
const reqMock = {};
const resMock = { send: sinon.spy() };
it('always responds with 200', (done) => {
helloWorld(reqMock, resMock);
expect(resMock.send.callCount).toBe(1);
expect(resMock.send).toHaveBeenCalledWith(200);
});

Testing is about building up confidence or trust.
I would start by Unit Testing the function in FireBase. Without more requirements defined, I would follow the documentation. Once those Unit Tests pass you can consider what type of testing you want at the Express level. Keeping in mind that you have already tested the function, the only thing to test at the Express level is if the mapping is correct. A few tests at that level should be sufficient to ensure that the mappings have not become "stale" as a result of some set of changes.
If you want to test the Express level and above without having to involve the DB, then you would look at a mocking framework to act like the database for you.
Hope this helps you think about what tests you need.

You can use postman application for unit test.
Enter following url with your project name
https://us-central1-your-project.cloudfunctions.net/hello
app.get('/hello/',(req, res) => {
res.send('hello world');
return 'success';
});

Related

Mock function in Firebase local emulator

Such as described here, I'm using local emulator (on-line) to make tests im my cloud functions.
Index.js:
var status = 200;
exports.saveAndSendMail = functions.https.onCall( async (req, res) => {
try{
let jsons = req.body;
await saveInfirestore(jsons);
await sendMail("Data saved", jsons);
} finally {
closeConnection(res, status);
}
async function saveInfirestore(json) {
//execute business logic and save in firestore (irrelevant for this question)
}
function closeConnection (res, status){
res.sendStatus(status);
res.end();
}
async function sendMail(title, message) {
try {
AWS.config.loadFromPath('./config_mail.json');
// Create sendEmail params
var params = {
Destination: {
ToAddresses: [
'mymail#gmail.com'
]
},
Message: { /* required */
Body: { /* required */
Html: {
Charset: "UTF-8",
Data: JSON.stringfy(message);
}
},
Subject: {
Charset: 'UTF-8',
Data: title
}
},
Source: '"Origin" <origin#gmail.com>',
ReplyToAddresses: [
'origin#gmail.com'
]
};
// Create the promise and SES service object
var sendPromise = new AWS.SES({apiVersion: '2022-17-01'}).sendEmail(params).promise();
}
catch(e){
throw e;
}
// Handle promise's fulfilled/rejected states
sendPromise.then(
function(data) {
console.log(data.MessageId);
}).catch(
function(err) {
console.error(err, err.stack);
});
}
index.test.js
const { expect } = require("chai");
const admin = require("firebase-admin");
const test = require("firebase-functions-test")({
projectId: process.env.GCLOUD_PROJECT,
});
const myFunctions = require("../index");
describe("Unit tests", () => {
after(() => {
test.cleanup();
});
it("test if save is correct", async () => {
const wrapped = test.wrap(myFunctions.saveAndSendMail);
const req = {
body: [{
value: 5,
name: 'mario'
}]
};
const result = await wrapped(req);
let snap = await db.collection("collection_data").get();
expect(snap.size).to.eq(1);
snap.forEach(doc => {
let data = doc.data();
expect(data.value).to.eql(5);
expect(data.name).to.eql('mario');
});
});
I execute it with: firebase emulators:exec "npm run test"
I have 2 problems.
1 - When execute, I receive the error TypeError: res.sendStatus is not a function. If I comment closeConnection call in block finally (index.js), this code run perfectly and all tests and "expect" run with success. But, this correct way is mock this method or mock 'res' calls. I tried mock with something like this:
const res = {
sendStatus: (status) => {
},
end: () => {
}
}
const result = await wrapped(req, res);
But, I receive this error:
Error: Options object {} has invalid key "sendStatus"
at /home/linuxuser/my-project/firebase/functions/myfolder/node_modules/firebase-functions-test/lib/main.js:99:19
at Array.forEach (<anonymous>)
at _checkOptionValidity (node_modules/firebase-functions-test/lib/main.js:97:26)
at wrapped (node_modules/firebase-functions-test/lib/main.js:57:13)
at Context.<anonymous> (test/index.test.js:50:26)
at processImmediate (node:internal/timers:464:21)
Problem 2:
I'm not wish receive an e-mail each time that tests executes. How I mock sendMail function?
Something very important to point out is that you are currently trying to use a Firebase callable function, as shown by the function heading functions.https.onCall(() => {});. Since you want to work with requests and response codes, the correct type of function to use is an HTTP function. You would only need to change the heading in your index.js:
exports.saveAndSendMail = functions.https.onRequest(async (req, res) => {
// function body
});
Now, your first problem can then be solved by correctly mocking the res object that is passed to the function (inside index.test.js). When testing HTTP functions, you must not use test.wrap() when calling the function, nor expect the result as you were doing with const result = await wrapped(req); This is since Wrap being only supported for testing onCall functions. You can see another snippet of how to call an HTTP function for testing in the documentation.
it("test if save is correct", async () => {
const req = {
body: [{
value: 5,
name: 'mario'
}]
};
// mocking the response object that is returned from the function:
const res = {
sendStatus: (code) => {
expect(code).to.eql(200); // asserting that we get 200 back as the response code
},
end: () => {
}
};
const result = await myFunctions.saveAndSendMail(req, res); // mocking a call to an HTTP function, without test.wrap()
// rest of the function…
For your second problem, I haven’t used AWS SES before, but it seems this library offers ways to mock the functions so that you won’t have to actually send emails during your tests.

Firebase cloud functions Appcheck for https.onRequest

As per documentation we can add appcheck as below,
exports.yourCallableFunction = functions.https.onCall((data, context) => {
// context.app will be undefined if the request doesn't include a valid
// App Check token.
if (context.app == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called from an App Check verified app.')
}
});
My question right now is how do I need to add app-check for below scenario?
exports.date = functions.https.onRequest((req, res) => {
});
In the client, get an appCheck token from Firebase. Send it in a header to your function. Get the token from the req object's headers. Verify the the token with firebase-admin. I'll include the documentation for the client below, then the gist of how I implemented it client side with Apollo-client graphql. Then I'll include the documentation for the backend, then the gist of how I implemented the backend, again with Apollo.
client (from the documentation):
const { initializeAppCheck, getToken } = require('firebase/app-check');
const appCheck = initializeAppCheck(
app,
{ provider: provider } // ReCaptchaV3Provider or CustomProvider
);
const callApiWithAppCheckExample = async () => {
let appCheckTokenResponse;
try {
appCheckTokenResponse = await getToken(appCheck, /* forceRefresh= */ false);
} catch (err) {
// Handle any errors if the token was not retrieved.
return;
}
// Include the App Check token with requests to your server.
const apiResponse = await fetch('https://yourbackend.example.com/yourApiEndpoint', {
headers: {
'X-Firebase-AppCheck': appCheckTokenResponse.token,
}
});
// Handle response from your backend.
};
client (gist from my implementation)
import { setContext } from "#apollo/client/link/context";
import { app } from '../firebase/setup';
import { initializeAppCheck, ReCaptchaV3Provider, getToken } from "firebase/app-check"
let appCheck
let appCheckTokenResponse
const getAppCheckToken = async () => {
const appCheckTokenResponsePromise = await getToken(appCheck, /* forceRefresh= */ false)
appCheckTokenResponse = appCheckTokenResponsePromise
}
const authLink = setContext(async (_, { headers }) => {
if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_ENV === 'production') {
appCheck = initializeAppCheck(app, {
provider: new ReCaptchaV3Provider('my_public_key_from_recaptcha_V3'),
isTokenAutoRefreshEnabled: true
})
await getAppCheckToken()
}
return {
headers: {
...headers,
'X-Firebase-AppCheck': appCheckTokenResponse?.token,
},
}
})
backend / server (from the documentation)
const express = require('express');
const app = express();
const firebaseAdmin = require('firebase-admin');
const firebaseApp = firebaseAdmin.initializeApp();
const appCheckVerification = async (req, res, next) => {
const appCheckToken = req.header('X-Firebase-AppCheck');
if (!appCheckToken) {
res.status(401);
return next('Unauthorized');
}
try {
const appCheckClaims = await firebaseAdmin.appCheck().verifyToken(appCheckToken);
// If verifyToken() succeeds, continue with the next middleware
// function in the stack.
return next();
} catch (err) {
res.status(401);
return next('Unauthorized');
}
}
app.get('/yourApiEndpoint', [appCheckVerification], (req, res) => {
// Handle request.
});
backend / server (gist from my implementation)
import { https } from 'firebase-functions'
import gqlServer from './graphql/server'
const functions = require('firebase-functions')
const env = process.env.ENV || functions.config().config.env
const server = gqlServer()
const api = https.onRequest((req, res) => {
server(req, res)
})
export { api }
. . .
import * as admin from 'firebase-admin';
const functions = require('firebase-functions');
const env = process.env.ENV || functions.config().config.env
admin.initializeApp()
appCheckVerification = async (req: any, res: any) => {
const appCheckToken = req.header('X-Firebase-AppCheck')
if (!appCheckToken) {
return false
}
try {
const appCheckClaims = await admin.appCheck().verifyToken(appCheckToken);
return true
} catch (error) {
console.error(error)
return false
}
}
. . .
const apolloServer = new ApolloServer({
introspection: isDevelopment,
typeDefs: schema,
resolvers,
context: async ({ req, res }) => {
if (!isDevelopment && !isTest) {
const appCheckVerification = await appCheckVerification(req, res)
if (!appCheckVerification) throw Error('Something went wrong with verification')
}
return { req, res, }
}
If you enforce app check in Cloud Functions it will only allow calls from apps that are registered in your project.
I'm not sure if that is sufficient for your use-case though, as I doubt most apps where you can provide a web hook will have implemented app attestation - which is how App Check recognizes valid requests.
You can generate an app check token in the client and verify the token in the server using firebase admin SDK. Here is the firebase documentation for the same
Firebase enable App check enforcement documentation teaches you that to validate the caller from your function you just need to check the context.app then gives you an example like this
exports.EXAMPLE = functions.https.onCall((data, context) => {});
https://firebase.google.com/docs/app-check/cloud-functions?authuser=0
But when you are deploying your function in the google cloud dashboard, you select HTTP FUNCTION -> nodejs 14 -> then you are directed to code like this
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = (req, res) => {
let message = req.query.message || req.body.message || 'Hello World!';
res.status(200).send(message);
};
My question when I saw this was: "How am i going to get a context if I only have request/response"
The answer is simple. YOU MUST SWITCH THE CONSTRUCTORS
You must re-write your function in a way that instead of dealing with req/res like any express function you are dealing with context/data
http functions are different of callable functions (the ones that deals with context/data)
IT IS SIMILAR BUT NOT EXACTLY EQUAL AND SOME MODIFICATIONS WILL BE NECESSARY.
mainly if your function deals with async stuff and have a delayed response you are going to need to rewrite many stuff
check this tutorial
https://firebase.google.com/docs/functions/callable

excel4node fetch request with next.js api routes not triggering download

I am generating an excel file and want it to be downloaded for the client by triggering an API route using the Next.js framework. I am having trouble triggering the download by using fetch. The download can be triggered by window.open(//urlhere, '_self') but the API call using fetch gives this response on request:
API resolved without sending a response for /api/download?Students= this may result in stalled requests.
The excel4node documentation says we can send an excel document through an API like this:
// sends Excel file to web client requesting the / route
// server will respond with 500 error if excel workbook cannot be generated
var express = require('express');
var app = express();
app.get('/', function(req, res) {
wb.write('ExcelFile.xlsx', res);
});
app.listen(3000, function() {
console.log('Example app listening on port 3000!');
});
Here is my backend download.js which lives in pages/api/:
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import Link from "next/link";
import getPartners from "../../components/main";
import excel from "excel4node";
export default function handler(req, res) {
const students = req.query.Students.split(",");
const partners = JSON.stringify(getPartners(students));
let workbook = createExcelList(partners);
workbook.write("PartnerList.xlsx", res);
}
const createExcelList = (partnersJSON) => {
const workbook = new excel.Workbook();
const partnersObject = JSON.parse(partnersJSON);
/* Using excel4node a workbook is generated formatted the way I want */
return workbook;
};
export const config = {
api: {
bodyParser: true,
},
};
And here is the function that is triggered on a button press in the front end.
const makePartners = async () => {
let queryStudents = studentList.join(",");
const url = "http://localhost:3000/api/download?Students=" + queryStudents;
if (studentList.length !== 0) {
try {
const res = await fetch(url, {
headers: {
"Content-Type":
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
},
});
console.log(res);
} catch (e) {
console.log(e);
}
}
};
Which does not trigger the download. But using window.open(url, '_self) does. So, I can trigger the download by changing the function to the following. However I don't think this is the correct way of doing things and would like to be able to understand how to use fetch correctly.
const makePartners = () => {
let queryStudents = studentList.join(",");
const url = "http://localhost:3000/api/download?Students=" + queryStudents;
if (studentList.length !== 0) {
window.open(url, "_Self");
}
};
I am not sure if this is a Next.js issue or not. Does anyone have any insight? Any help would be appreciated.

How to use Mollie Api with firebase cloud functions

I could need some help setting up the node.js api from mollie with firebase cloud functions. I tried to use parts of the cloudfunction setting up paypal guide but didn't get it to work yet. I'm new to cloud functions and node.js so i have a hard time getting it done. I am using hard coded payment properties for testing purposes. I am using a blaze subscribtion to be able to do requests to non-Google services The code i have so far:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
Mollie = require("mollie-api-node");
mollie = new Mollie.API.Client;
mollie.setApiKey("test_GhQyK7Gkkkkkk**********");
querystring = require("querystring");
fs = require("fs");
exports.pay = functions.https.onRequest((req, res) => {
console.log('1. response', res)
mollie.payments.create({
amount: 10.00,
method: Mollie.API.Object.Method.IDEAL,
description: "My first iDEAL payment",
redirectUrl: "https://dummyse-afgerond",
webhookUrl: "https://us-c90e9d.cloudfunctions.net/process",
testmode: true
}, (payment)=> {
if (payment.error) {
console.error('errrr' , payment.error);
return response.end();
}
console.log('3. payment.getPaymentUrl()', payment.getPaymentUrl());
res.redirect(302, payment.getPaymentUrl());
});
});
exports.process = functions.https.onRequest((req, res) => {
let _this = this;
this.body = "";
req.on("data", (data)=> {
console.log('_this.body += data', _this.body += data)
return _this.body += data;
});
req.on("end", ()=> {
console.log('hier dan?')
let mollie, _ref;
_this.body = querystring.parse(_this.body);
if (!((_ref = _this.body) !== null ? _ref.id : void 0)) {
console.log('res.end()', res.end())
return res.end();
}
})
mollie.payments.get(
_this.body.id
, (payment) => {
if (payment.error) {
console.error('3a. err', payment.error);
return response.end();
}
console.log('4a. payment', payment);
console.log('5a. payment.isPaid()', payment.isPaid());
if (payment.isPaid()) {
/*
At this point you'd probably want to start the process of delivering the
product to the customer.
*/
console.log('6a. payment is payed!!!!!!!!!!')
} else if (!payment.isOpen()) {
/*
The payment isn't paid and isn't open anymore. We can assume it was
aborted.
*/
console.log('6a. payment is aborted!!!!!!!!!!')
}
res.end();
});
});
this is mollies api guide:
https://github.com/mollie/mollie-api-node
this is paypal cloud function guide:
https://github.com/firebase/functions-samples/tree/master/paypal
UPDATE:
I updated the code. The error i get now is that all properties of the payment variable are undefined in the procces function (webhook). and the payment.isPaid() function says false while it should say true.
I did the same when I first tried to get mollie working in firebase cloud functions, this:
let _this = this;
this.body = "";
req.on("data", (data)=> {
console.log('_this.body += data', _this.body += data)
return _this.body += data;
});
req.on("end", ()=> {
console.log('hier dan?')
let mollie, _ref;
_this.body = querystring.parse(_this.body);
if (!((_ref = _this.body) !== null ? _ref.id : void 0)) {
console.log('res.end()', res.end())
return res.end();
}
})
is not necessary.
my webhook endpoint is much simpler, directly using the request & response that functions provides:
exports.paymentsWebhook = functions.https.onRequest((request, response) => {
// ...
console.log("request.body: ", request.body);
console.log("request.query: ", request.query);
mollie.payments.get(request.body.id, function (payment) {
console.log("payment", payment);
if (payment.error) {
console.error('payment error: ', payment.error);
response.end();
}
//checking/processing the payment goes here...
response.end();
});
});

How to use Sinon to stub out knex calls in Hapi/Lab test?

I'm trying to set up a testing pattern for a new Hapi app. I've used Mocha and Chai in the past with Express, but I'm trying to use Lab and Code to stay in the Hapi ecosystem. I'm also using Bookshelf and Knex to handle database interaction.
So I have a simple health endpoint I'd like to test.
'use strict';
const controller = require('../controller/healthController');
module.exports = [
{
method: 'GET',
path: '/health',
config: {
handler: controller.health,
description: 'The health endpoint returns 200',
tags: ['api', 'health']
}
}
];
In the handler it just does a quick query to make sure it can connect to the DB.
'use strict';
const bookshelf = require('../config/db');
const knex = bookshelf.knex;
module.exports = {
health: function (request, reply) {
knex.raw('SELECT version()').then(() => {
reply('service is running');
}).catch((err) => {
reply({err: err, code: 500});
});
}
};
As far as I understand it, requiring the server and then using server.inject doesn't actually start the server so I don't believe I should have a db connection, which would mean I should have to mock it out the db call. What is odd to me is that this test passes:
'use strict';
const Code = require('code');
const Lab = require('lab');
const lab = exports.lab = Lab.script();
const describe = lab.describe;
const it = lab.test;
const expect = Code.expect;
const before = lab.before;
let server;
describe('health controller', () => {
before((done) => {
server = require('../../server');
done();
});
it('health check replies 200 when successful call to db', (done) => {
const options = {
method: 'GET',
url: '/health'
};
server.inject(options, (res) => {
expect(res.payload).to.include('is running');
expect(res.statusCode).to.equal(200);
done();
});
});
});
So I have two problems. First, I feel like the test above shouldn't really pass. Unless it's loading everything up and thus connecting to the db I suppose. Maybe I should be testing just the controller/handler method? But I haven't found any examples of that.
Second, I've tried to stub out the knex.raw call anyway and when I try to do it like below I get a 500 error.
'use strict';
const Code = require('code');
const Lab = require('lab');
const Sinon = require('sinon');
const lab = exports.lab = Lab.script();
const describe = lab.describe;
const it = lab.test;
const expect = Code.expect;
const before = lab.before;
let server;
let knex = require('../../app/config/db').knex;
describe('health controller', () => {
before((done) => {
server = require('../../server');
done();
});
it('health check replies 200 when successful call to db', (done) => {
const stub = Sinon.stub(knex, 'raw').returns({});
const options = {
method: 'GET',
url: '/health'
};
server.inject(options, (res) => {
expect(res.payload).to.include('is running');
expect(res.statusCode).to.equal(200);
expect(stub.calledOnce).to.be.true();
done();
});
});
});
I'm not really sure why that's happening.
I think Sinon.stub(knex, 'raw').resolves({}); is a better solution
server.inject works exactly as if you made a request to a real server. So if your database is up when the test are run, the endpoint will return data from the database just like it does when you start the server manually.
Sinon.stub(knex, 'raw').returns({}); won't work. knex.raw(…) is expected to return a Promise, not an empty object. Please try the following:
Sinon.stub(knex, 'raw').returns(Promise.resolve({}));
Just a side note: Remember to call server.stop() after each test in order to ensure there's no state persisted between tests. In general, I think you can take a look at example test files in Hapi University repository.

Resources