function getUserValue(){
return new Promise((resolve, reject) => {
readLine("enter the number : ", (enter) => {
if(enter.length > 3){
throw new Error("some error")
}
)};
}
async function play(){
try{
await getUserValue();
} catch(message){
console.log(message);
}
}
- jest -
test("exception test", expect(() => {
const app = new App();
app.play();
}).toThrow();
I want to receive the value through "getUserValue" using readLine of nodejs.
And I wanted to use async function to deal with errors. I tired to test this code using jest's toThrow. But it doesn't receive Error and doesn't work...
please help me.. What did I do wrong?
I really want to know how to handle error.
Related
I'm testing my GraphQL api using Jest.
I'm using a separate test suit for each query/mutation
I have 2 tests (each one in a separate test suit) where I mock one function (namely, Meteor's callMethod) that is used in mutations.
it('should throw error if email not found', async () => {
callMethod
.mockReturnValue(new Error('User not found [403]'))
.mockName('callMethod');
const query = FORGOT_PASSWORD_MUTATION;
const params = { email: 'user#example.com' };
const result = await simulateQuery({ query, params });
console.log(result);
// test logic
expect(callMethod).toBeCalledWith({}, 'forgotPassword', {
email: 'user#example.com',
});
// test resolvers
});
When I console.log(result) I get
{ data: { forgotPassword: true } }
This behaviour is not what I want because in .mockReturnValue I throw an Error and therefore expect result to have an error object
Before this test, however, another is ran
it('should throw an error if wrong credentials were provided', async () => {
callMethod
.mockReturnValue(new Error('cannot login'))
.mockName('callMethod');
And it works fine, the error is thrown
I guess the problem is that mock doesn't get reset after the test finishes.
In my jest.conf.js I have clearMocks: true
Each test suit is in a separate file, and I mock functions before tests like this:
import simulateQuery from '../../../helpers/simulate-query';
import callMethod from '../../../../imports/api/users/functions/auth/helpers/call-accounts-method';
import LOGIN_WITH_PASSWORD_MUTATION from './mutations/login-with-password';
jest.mock(
'../../../../imports/api/users/functions/auth/helpers/call-accounts-method'
);
describe('loginWithPassword mutation', function() {
...
UPDATE
When I substituted .mockReturnValue with .mockImplementation everything worked out as expected:
callMethod.mockImplementation(() => {
throw new Error('User not found');
});
But that doesn't explain why in another test .mockReturnValue works fine...
Change .mockReturnValue with .mockImplementation:
yourMockInstance.mockImplementation(() => {
throw new Error();
});
in case you want to assert
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
If it's a promise you can also to .rejects www.jestjs.io/docs/en/asynchronous#resolves--rejects
For promises, can use https://jestjs.io/docs/mock-function-api#mockfnmockrejectedvaluevalue
test('async test', async () => {
const asyncMock = jest.fn().mockRejectedValue(new Error('Async error'));
await asyncMock(); // throws "Async error"
});
For testing that error was thrown or not, can use https://eloquentcode.com/expect-a-function-to-throw-an-exception-in-jest
const func = () => {
throw new Error('my error')
}
it('should throw an error', () => {
expect(func).toThrow()
})
For Angular + Jest:
import { throwError } from 'rxjs';
yourMockInstance.mockImplementation(() => {
return throwError(new Error('my error message'));
});
I'm writing an async test that expects the async function to throw like this:
it("expects to have failed", async () => {
let getBadResults = async () => {
await failingAsyncTest()
}
expect(await getBadResults()).toThrow()
})
But jest is just failing instead of passing the test:
FAIL src/failing-test.spec.js
● expects to have failed
Failed: I should fail!
If I rewrite the test to looks like this:
expect(async () => {
await failingAsyncTest()
}).toThrow()
I get this error instead of a passing test:
expect(function).toThrow(undefined)
Expected the function to throw an error.
But it didn't throw anything.
You can test your async function like this:
it('should test async errors', async () => {
await expect(failingAsyncTest())
.rejects
.toThrow('I should fail');
});
'I should fail' string will match any part of the error thrown.
I'd like to just add on to this and say that the function you're testing must throw an actual Error object throw new Error(...). Jest does not seem to recognize if you just throw an expression like throw 'An error occurred!'.
await expect(async () => {
await someAsyncFunction(someParams);
}).rejects.toThrowError("Some error message");
We must wrap the code in a function to catch the error. Here we are expecting the Error message thrown from someAsyncFunction should be equal to "Some error message". We can call the exception handler also
await expect(async () => {
await someAsyncFunction(someParams);
}).rejects.toThrowError(new InvalidArgumentError("Some error message"));
Read more https://jestjs.io/docs/expect#tothrowerror
Custom Error Class
The use of rejects.toThrow will not work for you. Instead, you can combine the rejects method with the toBeInstanceOf matcher to match the custom error that has been thrown.
Example
it("should test async errors", async () => {
await expect(asyncFunctionWithCustomError()).rejects.toBeInstanceOf(
CustomError
)
})
To be able to make many tests conditions without having to resolve the promise every time, this will also work:
it('throws an error when it is not possible to create an user', async () => {
const throwingFunction = () => createUser(createUserPayload)
// This is what prevents the test to succeed when the promise is resolved and not rejected
expect.assertions(3)
await throwingFunction().catch(error => {
expect(error).toBeInstanceOf(Error)
expect(error.message).toMatch(new RegExp('Could not create user'))
expect(error).toMatchObject({
details: new RegExp('Invalid payload provided'),
})
})
})
I've been testing for Firebase cloud functions and this is what I came up with:
test("It should test async on failing cloud functions calls", async () => {
await expect(async ()=> {
await failingCloudFunction(params)
})
.rejects
.toThrow("Invalid type"); // This is the value for my specific error
});
This is built on top of lisandro's answer.
If you want to test that an async function does NOT throw:
it('async function does not throw', async () => {
await expect(hopefullyDoesntThrow()).resolves.not.toThrow();
});
The above test will pass regardless of the value returned, even if undefined.
Keep in mind that if an async function throws an Error, its really coming back as a Promise Rejection in Node, not an error (thats why if you don't have try/catch blocks you will get an UnhandledPromiseRejectionWarning, slightly different than an error). So, like others have said, that is why you use either:
.rejects and .resolves methods, or a
try/catch block within your tests.
Reference:
https://jestjs.io/docs/asynchronous#asyncawait
This worked for me
it("expects to have failed", async () => {
let getBadResults = async () => {
await failingAsyncTest()
}
expect(getBadResults()).reject.toMatch('foo')
// or in my case
expect(getBadResults()).reject.toMatchObject({ message: 'foo' })
})
You can do like below if you want to use the try/catch method inside the test case.
test("some test case name with success", async () => {
let response = null;
let failure = null;
// Before calling the method, make sure someAsyncFunction should be succeeded
try {
response = await someAsyncFunction();
} catch(err) {
error = err;
}
expect(response).toEqual(SOME_MOCK_RESPONSE)
expect(error).toBeNull();
})
test("some test case name with failure", async () => {
let response = null;
let error = null;
// Before calling the method, make sure someAsyncFunction should throw some error by mocking in proper way
try {
response = await someAsyncFunction();
} catch(err) {
error = err;
}
expect(response).toBeNull();
expect(error).toEqual(YOUR_MOCK_ERROR)
})
Edit:
As my given solution is not taking the advantage of inbuilt jest tests with the throwing feature, please do follow the other solution suggested by #Lisandro https://stackoverflow.com/a/47887098/8988448
it('should test async errors', async () => {
await expect(failingAsyncTest())
.rejects
.toThrow('I should fail');
});
test("It should test async on failing cloud functions calls", async () => {
failingCloudFunction(params).catch(e => {
expect(e.message).toBe('Invalid type')
})
});
I have below code.
//utils.js
const cp = require('child_process');
module.exports.execuateBashCommand = ()=> {
return new Promise((resolve, reject)=> {
try {
cp.exec('bash bash.sh').stdout.on('data', (response)=> {
if (response.toString().indexOf("Success") > -1) {
//Some business logic
const result = "working";
resolve(result);
}
});
} catch (error) {
console.log("Error", error);
reject(error);
}
})
}
Here, there is an anonymous function that gets fired when a data event is emitted by stdout of exec process. I have no control over the output of bash.js, the desired response may take 5 to 10 seconds. Also I cannot add a timer and wait for this event to fire as I have mocked the exec response using sinon. So there is no real call to bash.js. So how can I emit this event manually and see if the business logic get computed? I am new to unit testing in Javascript. Currently I am using mocha and sinon for my unit testing requirements.
I have following test case, but it is not working as expected..
//utils.test.js
it("1. test case", (done)=> {
try {
const mockStdout = new events.EventEmitter();
mockStdout.on('data', ()=> {
return "Success";
});
mockStdout.emit('data');
sandbox.stub(cp.exec('test test'), 'stdout').returns(mockStdout);
utils.execuateBashCommand().then((result)=> {
assert(data, "working");
done();
});
} catch (error) {
console.log("Error in test cases: ", error);
}
})
Please advice.
I have tried couple of things. Finally, my test cases got passed by below solution.
it("1. test case", (done) => {
const fake = {
stdout: new EventEmitter(),
}
fake.stdout.on('data', () => { });
sinon.stub(cp, 'exec').returns(fake);
utils.execuateBashCommand().then((res) => {
expect(res, true);
done();
});
fake.stdout.emit('data', "Success");
})
I am not sure whether this is a right way or not. If anyone have better solution, please advice.
I am stuck on writing a unit test for when the redisClient fails for the createClient call. Any idea on how I can write this. Below you will find what I have so far.
const asyncRedis = require("async-redis");
class redis {
constructor(redisHost, redisPort) {
this.redisHost = redisHost;
this.redisPort = redisPort;
}
async init() {
try {
this.redisClient = asyncRedis.createClient({
port: this.redisPort,
host: this.redisHost
});
} catch(error) {
console.log(`Error creating client due to: ${error}`)
}
}
}
module.exports = redis;
redis-test.js
test('init on error', async () => {
jest.mock('../../src/redis/redis')
const redis = require('../../src/redis/Redis');
redis.mockImplementation(() => {
return {
init: jest.fn(() => { throw new Error(); }
)};
})
expect(await redis.init()).toThrowError(Error());
})
You are mocking your code, you should be mocking the async-redis library code.
You need to mock the createClient method to always throw an error. So that you can check your catch flow is executed.
This part, you got it right jest.fn(() => { throw new Error(); }, always return an error.
I am no expert in NodeJS, sorry I am not able to provide detailed source code.
I'm trying to test a simple function that generates a random name using the nodejs crypto library. I'm using sinon to stub out a method call within the callback of pseudoRandomBytes but the stub doesn't seem to be called. Example:
getFileName.js
const crypto = require('crypto');
module.exports = (req, file, cb) => {
crypto.pseudoRandomBytes(32, (err, raw) => {
try{
cb(err, err ? undefined : crypto.createHash('MD5').update(raw).digest('hex'));
} catch(err) {
cb(err);
}
});
};
Test (running in mocha)
it('Crypto Error: createHash', function () {
const crypto = require('crypto');
const expectedError = new Error('stub error occurred');
let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);
let callback = sinon.spy();
getFileName(null, null, callback);
cryptoStub.restore();
sinon.assert.calledWith(callback, expectedError);
});
I would expect the above test to throw once createHash gets called. If I move the crypto.createHash call outside of the callback (before the pseudoRandomNumber call) it works just fine. I a bit of a newbie so my basic understanding of what sinon and nodejs are doing could be completely wrong. Any help would be much appreciated.
The reason why it seems like createHash() wasn't called was because you were making an assertion before the callback call was complete due to asynchronous function.
Promise with async/await will work. Another method which doesn't involve changing your module to use promise is to do your assertions within the callback.
it('Crypto Error: createHash', function (done) {
const crypto = require('crypto');
const expectedError = new Error('stub error occurred');
let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);
getFileName(null, null, function (err, hash) {
sinon.assert.match(err, expectedError);
cryptoStub.restore();
done();
});
});
This way, you can check that the callback is called with the expected error. One way to confirm this is you can change line 4 to .throws('some other error') and the test will fail.
The problem is that crypto.pseudoRandomBytes() is an async function, so the rest of your test code executes before your callback. That way, your stub is restored before your function is actually used it.
In order to make it properly work, you should update your getFileName.js so it returns a promise - that way you can await it
module.exports = (req, file, cb) => {
return new Promise((resolve, reject) => {
crypto.pseudoRandomBytes(32, (err, raw) => {
try{
cb(err, err ? undefined : crypto.createHash('MD5').update(raw).digest('hex'));
resolve();
} catch(err) {
reject(cb(err));
}
});
});
};
and then in your test
// added async
it('Crypto Error: createHash', async () => {
const crypto = require('crypto');
const expectedError = new Error('stub error occurred');
let cryptoStub = sinon.stub(crypto, 'createHash').throws(expectedError);
let callback = sinon.spy();
await getFileName(null, null, callback);
// once we are here, the callback has already been executed and the promise that getFileName resolved.
cryptoStub.restore();
sinon.assert.calledWith(callback, expectedError);
});