I know it's something obvious that I'm missing but I'm trying to write unit tests using mocha, chai and sinon for an async handler that's referencing another async service. Here's the code for what I'm dealing with:
authenticateService.js
module.exports = (() => {
...
async authenticate(username, password) {
const isValid = await validCreds(username)
if (!isValid) {
throw Error(`invalid_credentials`)
}
return isValid
}
}
authenticateHandler.js
module.export.handler = {
async (event) => {
const auth = await authenticate(username, password)
if (auth) {
// do things
}
return {statusCodes.OK}
}
}
unit test
describe(`Admin Module`, () => {
beforeEach(() => {
sinon.stub(authenticateService.prototype, "authenticate")
.callsFake(() => Promise.resolve({status:200}))
})
afterEach(() => {
authenticationService.restore()
})
describe(`test auth`, () => {
it(`Receives GET event expects statusCode 200`, async () => {
const event = {
httpMethod: `POST`,
resourcePath: `/admin/authentications`,
body: {
username: `fizz#email.com`,
password: `buzz`,
recaptchaToken: `some_token`
},
}
expect(response.statusCode).to.equal(200)
})
})
I tried setting the following:
setting it in a sandbox
using yields, resolve, return and could not figure out how to stub it correctly
tried mocking the nested authenticateService, mocking the handler and mocking both
I keep getting Error: Trying to stub property 'authenticate' of undefined so I know it must not be instantiating the service correctly. Frankly it seems like a simple thing but I just cannot figure out what I am missing.
Related
Hi I'm trying to test a post route that make loots of operations using Jest.
I'm trying to mock a class that makes a http call to another API. The problem is the response is always undefined and I have no idea whats wrong.
This is my mock comunicante-service.js (located in root/mock):
module.exports = {
getConfig: jest.fn(async () => {
Promise.resolve(dadosContaConfig);
}),
sendMessage: jest.fn(async () => {
Promise.resolve('ok');
}),
};
And that's the test
jest.mock('../src/services/comunicante-service');
describe('test /message', () => {
it('no token', async () => {
const res1 = await request(app).post('/message').set(config);
expect(res1.statusCode).toEqual(400);
});
inside of the code that handle the /result there's a call to
const aux = await comunicanteService.getConfig(conta_id);
It looks like you may have forgotten to return something from the mock service. You could try:
module.exports = {
getConfig: jest.fn(async () => {
return Promise.resolve(dadosContaConfig);
}),
sendMessage: jest.fn(async () => {
return Promise.resolve('ok');
}),
};
This can be simplified to:
module.exports = {
getConfig: jest.fn().mockResolvedValue(dadosContaConfig),
sendMessage: jest.fn().mockResolvedValue('ok'),
};
I am trying to implement jest unit testing for firebase functions in my application. I was purely looking at implementing an offline test. I have mocked the request and response objects and am still trying to access some other firebase resources on execution. I am not sure whether I missed any sort of mocking. I intend to not use any firebase-related configuration URLs.
Here is my function and test samples,
randomgenerator.ts
import { https } from "firebase-functions";
export const generateTrickyNumber = https.onRequest(async (req, res) => {
corsHandler(req, res, async () => {
try {
let num =0;
switch (req.body.type) {
case "palindrome":
num= await generatePalindrome(req.body.startnumber,req.body.endnumber);
break;
case "prime":
num= await generatePrime(req.body.startnumber,req.body.endnumber);
break;
default:
break;
}
return res.status(200).send(num);
} catch (err) {
console.log({ err });
return;
}
});
});
randomgenerator.test.ts
import 'jest';
import * as httpMock from 'node-mocks-http';
import * as TestFunctions from 'firebase-functions-test';
const testEnv = TestFunctions();
import { generateTrickyNumber } from "../src/randomgenrator.ts";
describe('generateTrickyNumber ', () => {
test('it returns a successful response with a valid card', async () => {
const req = { method: "POST", body: {type:"palindrome", startnumber:1,endnumber:200} };
const res = {
send: (payload) => {
expect(payload).toBe(153)
},
};
await generateTrickyNumber (req as any, res as any);
});
test('it returns an error if the method is not passed', async () => {
const req = { body: { type: "palindrome" } };
const res = {
send: (payload) => {
expect(payload).toBe('Missing value!')
},
};
await generateTrickyNumber (req as any, res as any);
});
});
Do we need to handle any more mocking for making this successful? Please feel free to suggest.
I want to assert that emit from the EventEmitter class was called with specific parameters by using Jest. I have a separate file where I create an instance of eventEmitter to be used, and on the other class I import it and at some point the event is emitted.
// commonEmitter.ts
const events = require('events');
export const commonEmitter = new events.EventEmitter();
// class.ts
import { commonEmitter } from (..)
export class MyClass {
(...)
method(){
commonEmitter.emit('eventName', { data: true});
}
}
// class.spec.ts
let commonEmitterMock: any
beforeEach(() => {
commonEmitterMock = createMock('emit');
});
it('testMyClass', async () => {
const method = new MyClass().method();
expect(commonEmitterMock).toHaveBeenCalledWith('eventName', { data: true})
}
With this implementation the emit event is never called..
Cant figure out why, any idea?
To test different branches of your http request events without giving up on a over-engineered code you can do the follow.
This is a stub version of the function I intend to test using Jest:
function myRequest(resolve, reject) {
http.request(url, options, (res: IncomingMessage) => {
response.on('data', (chunk) => {
// On data event code
})
response.on('end', () => {
// On end event code
resolve()
})
response.on('error', (err) => {
reject(err)
})
}
}
Firstly, we need to mock the http library an overwrite the request implementation to manually trigger the callback and inject our mocked response object:
...
const mockRes = {
write: jest.fn(),
on: jest.fn(),
end: jest.fn()
}
jest.mock('http', () => ({
request: jest.fn().mockImplementation((url, options, cb) => {
cb(mockRes)
})
})
Then, each of our jest test unit, we manually trigger the callbacks on each of the events we desire to test passing data to each of the specific callbacks:
it('should call request callback and reject for invalid content response', async () => {
const resolve = jest.fn()
const reject = jest.fn()
mockRes.on.mockImplementation((event, cb) => {
if (event === 'end') {
cb()
} else if (event === 'data') {
cb(new Error('invalid_json_string'))
}
})
// #ts-ignore
myRequest(resolve, reject)
// #ts-ignore
expect(mockRes.on).toHaveBeenCalledWith('data', expect.any(Function))
expect(mockRes.on).toHaveBeenCalledWith('end', expect.any(Function))
expect(reject).toHaveBeenCalledWith(expect.any(Error))
})
It is better to inject the dependency to make your class more testable instead of importing it. So your class will look like
export class MyClass {
constructor(commonEmitter) {
this.commonEmitter_ = commonEmitter;
}
method(){
this.commonEmitter_.emit('eventName', { data: true});
}
}
And then your test file could be
let commonEmitterMock: any
beforeEach(() => {
commonEmitterMock = createMock('emit');
});
it('testMyClass', async () => {
const method = new MyClass(commonEmitterMock).method();
expect(commonEmitterMock).toHaveBeenCalledWith('eventName', { data: true})
}
I'm using nodejs exif library to retrieve metadata from JPEG files.
this lib is used this way :
import * as exif from 'exif'
new exif.ExifImage('path_to_file.jpg', function(err, metadata){ ... })
I've found everywhere how to stub a class method using sinon, pretty simple.
But I don't get how to stub this class constructor so that metadata (or err if I want to test failing case) will be the stubbed value I need to perform my test.
We can still use Sinon with callsFake function. Here is the example:
// src.js
const exif = require("exif");
function readImage() {
// convert to promise for easier testing
return new Promise((resolve, reject) => {
new exif.ExifImage("path_to_file.jpg", function(err, metadata) {
if (err) {
reject(err);
}
resolve(metadata);
});
});
}
module.exports = { readImage };
meanwhile for test
// test.js
const sinon = require('sinon');
const src = require('./src');
const exif = require('exif');
const expect = require('chai').expect;
describe('test exifimage', () => {
let exifStub;
beforeEach(function() {
exifStub = sinon.stub(exif, 'ExifImage')
})
afterEach(function() {
sinon.restore();
})
it('test when success', async () => {
const metadata = 'cinta';
// mock ExifImage as similar as its function signature
exifStub.callsFake((filename, callback) => {
callback(null, metadata); // error is set as null then we set metadata
});
const response = await src.readImage();
expect(response).to.equal(metadata);
});
it('test when error', async () => {
const errorMessage = 'this is error';
exifStub.callsFake((filename, callback) => {
callback(errorMessage); // specify error, error is defined as first param
});
try {
await src.readImage();
} catch (error) {
expect(error).to.equal(errorMessage);
}
});
});
Hope it helps
I have a lib say 'services.js'
class Service {
static doSomething() {
return Promise.resolve({});
}
}
I have another handler 'handler.js'
let Service = require('./Service');
exports.search = (req, res) => {
Service.doSomething().then(result => {
res.send(result);
}).catch(err=>{
res.status(500).send(err);
});
}
I want to test my handler. To do so I tried stubbing the static method in Service class like:
let Service = require(path to services.js),
Handler = require(path to handler.js),
http_mocks = require('node-mocks-http'),;
describe("handler tests : ", () => {
before(()=>{
sinon.stub(Service, 'doSomething').callsFake(()=>{});
})
it('should succeed', (done) => {
let response = buildResponse();
let request = http_mocks.createRequest({
method: 'GET',
url: '/search?q=2',
});
response.on('end', function() {
let result = JSON.parse(response._getData());
//Some validation
done();
});
Handler.search(request, response);
done();
})
})
I get TypeError: Service.doSomething is not a function. Is there an alternative? I tried using mockery as well. Am I missing something
I found a bug in the test code. Service.doSomething() returns promise but in the test file, we stub it using callsFake and it doesn't return promise
We might use resolves for this as in
before(() => {
sinon.stub(Service, 'doSomething').resolves('asik');
});
NOTE: resolves has been supported since Sinon 4.