sinon: Cannot stub non-existent own property formatData - node.js

I'm getting the error 'Cannot stub non-existent own property formatData' but i've honestly no idea why! I'm quite new to testing and this test is the same as another one i've done which worked.
const submitDetails = require('../src/scripts/submitDetails')
const sendEmail = require('../src/lib/sendEmail')
describe('submitDetails', function () {
let sandbox = null
before(() => {
sandbox = sinon.createSandbox()
})
afterEach(() => {
sandbox.restore()
})
describe('submitDetails', () => {
let mockParams, result
beforeEach(async () => {
mockParams = {
emailName: 'Confirmation',
formName: 'Contact'
}
sandbox.stub(submitDetails, 'formatData').returns({})
result = await submitDetails.formatData(mockParams)
})
it('should call formatData', () => {
expect(submitDetails.formatData).to.be.calledWith(mockParams)
})
it('should return lowercase params', () => {
expect(result).to.deep.equal({
emailName: 'confirmation',
formName: 'contact'
})
})
it('should call sendEmail', () => {
expect(sendEmail.sendEmail).to.be.calledWith(result)
})
})
describe('formatData', () => {})
})

Related

Change jest mock on class for single test

I have an issue where I want to change what a class method returns for a single test while testing a different module. I have the following:
testingModule.test.js
const { testingModuleMethod } = require('../testingModule')
jest.mock('../helperClass', () =>
jest.fn().mockImplementation(() => ({
helperClassMethod: jest.fn()
}))
);
describe('testingModule.js', () => {
describe('testingModuleMethod', () => {
describe('when errors', () => {
const consoleSpy = jest.spyOn(console, 'error');
// SOMETHING NEEDS TO GO HERE TO CHANGE THE jest.mock ON LINE 3
await expect(testingModuleMethod(data)).rejects.toThrow('Error');
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
});
});
testingModule.js
const HelperClass = require('./helperClass');
const testingModuleMethod = async (data, callback) => {
try {
const objectToEvaluate = data.object;
const helperClassInstance = new HelperClass();
await helperClassInstance.helperClassMethod(objectToEvaluate);
log('info', "Success!");
callback(null, {});
} catch(error) {
log('error', 'Something went wrong')
}
};
No matter what I put in there I either get an error with the code (undefined) or it just ignores it and resolves due to the mock at the start. I have tried adding a spy as well as importing the class and using the prototype override.
I'm using node and "jest": "^27.0.6"
I have managed to answer this by doing the following:
Firstly I discovered that to mock a class like that I have to add a jest function into the mock like so:
describe('testingModuleMethod', () => {
describe('when errors', () => {
const consoleSpy = jest.spyOn(console, 'error');
HelperClass.mockImplementation(() => ({
helperClassMethod: jest.fn(() => { throw new Error('Error') })
}));
await expect(testingModuleMethod(data)).rejects.toThrow('Error');
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
});
This also had a knock on effect to the rest of the tests though so I added a beforeEach at the start that looks like:
HelperClass.mockImplementation(
jest.fn().mockImplementation(() => ({
helperClassMethod: jest.fn()
}))
);
Finally I needed to require the class. The overall test looks like this now and works:
const { testingModuleMethod } = require('../testingModule');
const HelperClass = require('./helperClass');
jest.mock('../helperClass', () =>
jest.fn().mockImplementation(() => ({
helperClassMethod: jest.fn()
}))
);
describe('testingModule.js', () => {
beforeEach(() => {
HelperClass.mockImplementation(
jest.fn().mockImplementation(() => ({
helperClassMethod: jest.fn()
}))
);
});
describe('testingModuleMethod', () => {
describe('when errors', () => {
const consoleSpy = jest.spyOn(console, 'error');
HelperClass.mockImplementation(() => ({
helperClassMethod: jest.fn(() => { throw new Error('Error') })
}));
await expect(testingModuleMethod(data)).rejects.toThrow('Error');
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
});
});

Jest - how to mock a Class used by a module?

I have a class :
class RequestTimeout {
constructor(timeoutMilliseconds) {
this.timeoutMilliseconds = timeoutMilliseconds;
this.timeoutID = undefined;
}
start() {
return new Promise((resolve, reject) => {
this.timeoutID = setTimeout(() => reject(new Error(`Request attempt exceeded timeout of ${this.timeoutMilliseconds}`)), this.timeoutMilliseconds);
});
}
clear() {
if (this.timeoutID) clearTimeout(this.timeoutID);
}
}
module.exports = RequestTimeout;
This class is used in a module:
const RequestTimeout = require('./request-timeout');
function Request() {
...
async function withTimeout(request, ms) {
const timeout = new RequestTimeout(ms);
return Promise.race([
request(),
timeout.start(),
])
.then(
response => {
timeout.clear();
return response;
},
err => {
timeout.clear();
throw err;
}
);
}
...
}
How do i mock RequestTimeout in a test using Request? For example:
it('should clear the timeout following a successful response', async () => {
nock('http://example.com')
.get('/')
.reply(200, { example: true });
const response = await request.get({ ...baseOptions });
expect(response.example).toEqual(true);
});
// MOCK
let mockGetTimeOutId = jest.fn();
jest.mock('../request-timeout', () => {
return jest.fn().mockImplementation((ms) => {
let timeoutId = undefined;
return {
start: () => new Promise((resolve, reject) => {
timeoutId = setTimeout(() => reject(), ms);
}),
clear: () => mockGetTimeOutId(timeoutId),
}
})
});
// TEST
it('should clear the timeout following a successful response', async () => {
nock('http://example.com')
.get('/')
.reply(200, { example: true });
expect(mockGetTimeOutId).toHaveBeenCalledTimes(0);
const response = await request.get({ ...baseOptions });
expect(mockGetTimeOutId).toHaveBeenCalledTimes(1);
expect(response.example).toEqual(true);
});

Fail mocha test in catch block of rejected promise

How to fail the test in catch block of promise rejection when making http call using axios?
Adding expectations, asserts, should expressions in catch block doesn't help.
The test is passing.
I's run using .\node_modules\.bin\mocha
let chai = require('chai');
var expect = chai.expect;
var axios = require('axios')
var instance = axios.create({})
describe('test', () => {
context('test', () => {
it('should succeed', () => {
let url = 'url'
instance.get(url)
.then(function(response) {
expect(response.data).not.to.be.null
} )
.catch(function(err) {
console.error(err.data)
// should fail the test
})
})
})
})
If You want to verify my suggestions, replace url value with valid url (ex: https://google.com)
You can try several ways:
1) Using assert.fail()
const axios = require('axios');
const { assert, expect } = require('chai');
const instance = axios.create({})
describe('test', () => {
context('test', () => {
it('should succeed', () => {
let url = 'abc'
return instance.get(url)
.then((res) => {
expect(res.data).not.to.be.null;
})
.catch((err) => {
assert.fail('expected', 'actual', err);
});
});
});
});
2) Using done() with error object
const axios = require('axios');
const { expect } = require('chai');
const instance = axios.create({})
describe('test', () => {
context('test', () => {
it('should succeed', (done) => {
let url = 'abc'
instance.get(url)
.then((res) => {
expect(res.data).not.to.be.null;
done();
})
.catch((err) => {
done(err);
});
});
});
});
3) Simply just throw an error :)
const axios = require('axios');
const { expect } = require('chai');
const instance = axios.create({})
describe('test', () => {
context('test', () => {
it('should succeed', () => {
let url = 'abc'
return instance.get(url)
.then((res) => {
expect(res.data).not.to.be.null;
})
.catch((err) => {
throw err;
});
});
});
})
If You want to check if that method fails at all and You expect this, go that way (it requires chai-as-promised package):
const axios = require('axios');
const chai = require('chai');
chai.use(require('chai-as-promised'));
const instance = axios.create({})
describe('test', () => {
context('test', () => {
it('should succeed', () => {
let url = 'abc'
return chai.expect(instance.get(url)).to.be.rejected;
});
});
});

Async Await Node js

I am learning async await using node js
var testasync = async () =>
{
const result1 = await returnone();
return result1;
}
testasync().then((name)=>{
console.log(name);
}).catch((e) =>{
console.log(e);
});
var returnone = () =>{
return new Promise((resolve,reject)=>
{
setTimeout(()=>{
resolve('1');
},2000)
})
}
It fails with returnone is not a function. What am i doing wrong? calling the function just by itself work
returnone().then((name1) => {
console.log(name1)
})
Just calling the above code works
The reason you are getting this error because of hoisting. Your code seen by JS would look like this
var returnone;
var testasync = async () => {
const result1 = await returnone();
return result1;
}
testasync().then((name) => {
console.log(name);
}).catch((e) => {
console.log(e);
});
returnone = () => {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve('1');
}, 2000)
})
}
So the value of returnone is undefined.
You are assigning the function to the variable returnone at the end of the code, but you are trying to call that function before this assignment. You have two options to fix the code:
Option 1
Use a function declaration; this way, the function is hoisted and you can use it right from the start:
var testasync = async () => {
const result1 = await returnone();
return result1;
}
testasync().then((name) => {
console.log(name);
}).catch((e) => {
console.log(e);
});
function returnone() {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve('1');
}, 2000)
})
}
Option 2
Assign the function to the variable before you try to call it:
var returnone = () => {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve('1');
}, 2000)
})
}
var testasync = async () => {
const result1 = await returnone();
return result1;
}
testasync().then((name) => {
console.log(name);
}).catch((e) => {
console.log(e);
});

How to properly stub out request-promise in a mocha unit test using sinon?

My unit test is:
describe.only("Validator Service Tests", function () {
let request
before((done) => {
request = sinon.stub()
done()
})
beforeEach(() => {
process.env.API_URL = "http://test"
})
it('Should return with no errors if the file matches the schema', () => {
const updateStatusSpy = sinon.spy(FileLib, 'updateStatus')
request.yields({message: 'ok'})
return ValidatorService.handleMessage({
file: 'test'
})
.then((response) => {
assert()
console.log(response)
sinon.assert.calledOnce(updateStatusSpy)
assert(response, 'f')
})
})
})
The problem is my handleMessage function, which looks like:
exports.handleMessage = (message, done) => {
return stuff()
.then((result) => {
console.log('result', result)
if(result) {
return FileLib.updateStatus(fileId, 'valid')
}
return FileLib.updateStatus(fileId, 'invalid')
})
.then(done)
}
And my updateStatus function:
exports.updateStatus = function(fileId, status) {
console.log(fileId, status)
return request.put({
uri: `${process.env.API_URL}/stuff/${fileId}`,
body: {
status: status
}
})
}
My actual request call is buried so deep in, how can I stub it out when testing?
I'm not sure I completely understand your question, but if you are just trying to stub put, try something like this:
let stub;
beforeEach(() => {
putStub = sinon.stub(request, 'put').resolves('some_val_or_object'); //or yields or callsFake, depending on what you're using
});
it('should call request with put', async () => {
await //call your code
expect(putStub.called).to.be.true;
expect(putStub.calledWith(whatever_you_want_to_check)).to.be.true;
});

Resources