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.
Related
After the upgrade, Mocha can not even run a simple test here is the code
const assert = require('assert');
it('should complete this test', function (done) {
return new Promise(function (resolve) {
assert.ok(true);
resolve();
})
.then(done);
});
I took this code from here
I understood that it now throws an exception Error: Resolution method is overspecified. Specify a callback * or * return a Promise; not both.
But how to make it work? I did not understand. I have
node -v 6.9.4
mocha -v 3.2.0
How to run this code are now in a new and correct format?
Just drop
.then(done); and replace function(done) with function()
You are returning a Promise so calling done is redundant as it said in error message
In the elder versions you had to use callback in case of async methods like that
it ('returns async', function(done) {
callAsync()
.then(function(result) {
assert.ok(result);
done();
});
})
Now you have an alternative of returning a Promise
it ('returns async', function() {
return new Promise(function (resolve) {
callAsync()
.then(function(result) {
assert.ok(result);
resolve();
});
});
})
But using both is misleading
(see for example here https://github.com/mochajs/mocha/issues/2407)
Mocha allows to either use a callback:
it('should complete this test', function (done) {
new Promise(function (resolve) {
assert.ok(true);
resolve();
})
.then(done);
});
OR return a promise:
it('should complete this test', function () {
return new Promise(function (resolve) {
assert.ok(true);
resolve();
});
});
// Or in the async manner
it('should complete this test', async () => {
await Promise.resolve();
assert.ok(true);
});
You can't do both.
I had to removed the done from the function parameter and the done() of the function call
Before
before(async function (done) {
user = new User({ ...});
await user.save();
done()
});
After
before(async function () {
user = new User({ ...});
await user.save();
});
These works for me
I had this same issue. A lot of times Mocha is paired with another library called Chai. Chai has a package called "chai-as-promised". It gives you the super simple ability to write less code and test promises. In your case of just testing if a promise resolves, it seems perfect.
const chai = require('chai');
const chaiAsPromised = require("chai-as-promised");
const should = require("chai").should();
chai.use(chaiAsPromised);
describe("Testing with correct syntax and non repeated names", () => {
it("Should give us a positive response", () => {
graphQL.sendToGQL(model,"specialEndpoint").should.eventually.be.an("Object");
})
})
An example of async functions with done breaking.
Failure Case
it('If the credentials exists in the system it should return the token generated against it.', async (done) => {
let aObj = await admin.createAdmin();
chai.request(server)
.post("/authenticate")
.set("Content-Type", "application/x-www-form-urlencoded")
.send({username: aObj.login,password:aObj.password})
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("string");
done();
});
});
Success Case
it('If the credentials exists in the system it should return the token generated against it.', async () => {
let adminObj = await admin.createAdmin();
chai.request(server)
.post("/auth/login")
.set("Content-Type", "application/x-www-form-urlencoded")
.send({username: adminObj.login,password:adminObj.password})
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("string");
// done();
});
});
If you don't have callbacks, prior answers (which suggest deleting the done) is correct.
If need to both await some external promise, and then exercise a callback/errback-based implementation in your test, that solution doesn't help you.
You can use a library like pify to convert the callback API to use promises.
Alternatively, you can use a Latch in your callback:
it("test", async () => {
const l = new Latch()
const v = await promiseValue()
s.methodThatTakesCallback((err, result) => {
expect(result).to.eql(expected)
l.resolve() // < notifies mocha your test is done
})
return l.promise
})
In TypeScript, here's a very stripped-down Latch implementation:
/**
* Simple one-count concurrent barrier
*/
export class Latch {
readonly promise: Promise<void>
resolve!: () => void
constructor() {
this.promise = new Promise<void>(resolve => (this.resolve = resolve))
}
}
Just emit done callback completely and use async instead.
(This implementation is based on an express api running on firebase functions, using a custom jsonwebtoken)
const { FIREBASE_UID } = require('dotenv').config()?.parsed
const chai = require('chai');
const chaiHttp = require('chai-http');
const server = require('../lib/api').API;
const should = chai.should();
const expect = chai.expect
chai.use(chaiHttp)
const test = chai.request(server).keepOpen()
// get your token with an earlier mock request and store to a var
describe('Just checking a token', () => {
let some_token
it('should print custom jwt for testing, status: 200'), async () => {
try {
const res = await test.get(`/createCustomFirebaseToken/${FIREBASE_UID}`).send()
res.should.exist
res.should.have.status(200);
res.should.have.json
some_token = (JSON.parse(res.text)).token
} catch (error) {
throw error
}
}
it('should print details:PING, status:200'), async () => {
try {
const res = await test.get('/').set('Authorization',`Bearer ${some_token}`)
.send()
res.should.exist
res.should.have.status(200);
res.should.have.json
const { details, status } = JSON.parse(res.text)
expect(details).to.equal('PING')
expect(status).to.equal(200)
} catch (error) {
throw error
}
}
after(() => test.close())
})
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.
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
So I have a file, user-database, that looks something like this :
export function foo(id: number): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
findSomething(id)
.then((data) => {
//do something with data
})
}
}
export function findSomething(id: number): Promise<Object> {
return new Promise<Object> ((resolve, reject) => {
let query = 'SELECT * FROM user';
db.executeQuery(query);
.then(data) => {
if(data.length < 1) { reject(new Error('whoops')); }
resolve(data);
}, (err) => {
reject(err);
})
})
}
So I am writing unit tests using Sinon for the exterior function, foo, and therefore I want to stub the function it calls, findSomething. I do this as follows:
import * as user_db from '../../src/user-database';
describe('POST /someEndpoint', () => {
describe('when successful', () => {
let stub;
beforeEach(function() {
stub = sinon.stub(user_db, 'findSomething');
});
afterEach(function() {
stub.restore();
});
it('should respond with 200', function(done) {
stub.returns(anObjectIPredefine);
request(server)
.post(basePath)
.send(aPayloadIPredefine)
.expect(200, done);
});
}
}
When I run the test, I don't see the object I am telling the stub to return with this stub.returns(anObjectIPredefine). I instead actually have the function findSomething execute as normal and grab data from the dB. Is there anything obvious I am doing wrong? My only guess is that stub = sinon.stub(user_db, 'findSomething') is not the proper syntax for stubbing a function defined in the same scope as the function being tested. I can't find what an alternative syntax would be though.
So what I ended up doing was moving the functions I wished to stub to a different file. When this is done, stubbing works as intended. Probably not the best solution, but definitely a quick band-aid for anyone in a similar situation.