I have MongoDB Query function which in which query params are validated
Here is the function
Note: user is mongoose model
function fetchData(uName)
{
try{
if(isParamValid(uName))
{
return user.find({"uName":uName}).exec()
}
else {
throw "Invalid params"
}
}
catch(e)
{
throw e
}
}
To test this with invalid username values, I have written test code for that using mocha, chai, and chai-as-promised for promise-based functions
describe('Test function with invalid values', async ()=>{
it('should catch exception', async () => {
await expect(fetchData(inValidUserName)).to.throw()
})
it('should catch exception', async () => {
await expect(fetchData(inValidUserName)).to.throw(Error)
})
it('should catch exception', async () => {
await expect(fetchData(inValidUserName)).to.be.rejectedWith(Error)
})
it('should catch exception', async () => {
await expect(fetchData(inValidUserName)).to.be.rejected
})
})
None of them pass the test, How do I write a test case to handle exception for invalid userName values
You are passing the result of fetchData function call to expect function. Instead of calling the fetchData function inside expect function, pass a function to expect function.
it('should catch exception', async () => {
await expect(() => fetchData(inValidUserName)).to.throw('Invalid params')
})
Use try/catch
it('should catch exception', async () => {
try {
await fetchData(inValidUserName);
} catch(error) {
expect(error).to.exist;
}
})
Related
I have a basic stringify function that looks like this ->
export const stringify = <T>(value: T) => {
try {
return JSON.stringify(value);
} catch(error){
return ''
}
}
I want to write a test that can cover the catch block of the function.
I've tried adding such a test ->
it('should be able to check for errors', async () => {
await expect(stringify('')).rejects.toThrow()
})
But this test keeps throwing errors about the function not being a promise. The function isn't going into the catch block at all.
The main function isn't a promise so I can't use the promise functions of jest.
How do I test the catch block?
There is no need to use async/await in this test. Also when there is an error you are returning '' from catch block, meaning your function will not throw anything.
Something like will work for your case
it('should be able to check for errors', () => {
expect(stringify(<error value>)).toBe('')
})
Expect the function definition to Throw
const functionDef = () => {
throw new TypeError("Error Message");
};
test("Test description", () => {
expect(functionDef).toThrow(TypeError);
expect(functionDef).toThrow("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'm doing some functional testing with mocha. I stored my functions name, parameters and sucess message in local JSON file. I am checking my database response with local JSON response.
I'm using .then and .catch everywhere. I am looking to clean up a code with async await. How I can use async await here?
it('Check Authentication', (done) => {
readFileData('checkAuth').then(({ params, message}) => {
login.checkAuth({ ...params })
.then((result) => {
assert(result.message === message);
done();
})
.catch((err) => done(err));
});
});
Something like this. Haven't tested it tho. Basically instead of .then() you just await the call. Note that there is async before (done) callback. .catch() can be caught with try/catch block.
it('Check Authentication', async (done) => {
let response = await readFileData('checkAuth');
try {
let message = await login.checkAuth({ ...response.params }); // or w/e the response is
// assert the message
} catch (e) {
// do something with the error
}
});
Changed callback function to async to use `await
Wrapped all await calls in try-catch block to handle errors
Used const for params, message and result variables but if you are going to reassign values later in the code you can use let instead.
done() will be async call. Add await in front of that if you need that too to be sync call
it('Check Authentication', async (done) => {
try {
const { params, message } = await readFileData('checkAuth');
const result = await login.checkAuth({ ...params });
assert(result.message === message);
done();
} catch (err) {
done(err);
}
});
I created a class called API and it's a simple wrapper around Axios
export class API {
static get = async (route: string, version: string = API_VERSION) => {
try {
return await axios.get(`${BASE_URL + version}${route}`);
} catch (error) {
throw error;
}
};
}
I'm trying to test the catch branch of the get method:
I tried:
describe('API Throws Errors', () => {
beforeEach(() => {
// axios.get.mockImplementation(() => Promise.reject('rejected'));
jest.mock('axios', () => ({
get: jest.fn().mockReturnValue(Promise.reject('error'))
}));
});
it('get fails', async () => {
await expect(() => {
API.get(GROUPS.url());
}).rejects.toEqual('error');
});
afterEach(() => {
jest.clearAllMocks();
});
});
You can mock behaviour of axios.get by using jest.mock. Put the below code above the describe section:
jest.mock('axios', () => ({
get: jest.fn().mockReturnValue(Promise.reject('error'))
}));
And you test the error like below:
it('get fails', async () => {
await expect(API.get("bad_url")).rejects.toEqual('error');
});
Exact Code
jest.mock('axios', () => ({
get: jest.fn().mockReturnValue(Promise.reject('error')),
}));
describe('API Throws Errors', () => {
it('get fails', async () => {
await expect(API.get(GROUPS.url())).rejects.toEqual('error');
});
});
Note:
If you have another test case that shouldnt be failed, you can just mock it to return Promise.resolve(). Or you can just simple clear the mock.
describe('API Throws Errors', () => {
it('get fails', async () => {
await expect(API.get(GROUPS.url())).rejects.toEqual('error');
});
it('should success', async () => {
Axios.get.mockReturnValue(Promise.resolve(SOME_VALUE));
await expect(API.get(GROUPS.url())).resolves.toEqual(SOME_VALUE);
});
});
toThrowError() is supposed to be asserted against a function and not result because if an error happens on function call, expect doesn't have a chance to be evaluated. It's applicable only to regular functions where an error is thrown. It's not applicable to async functions because they don't throw an error but return a result, which rejected promise.
rejects.toThrowError() construction is a way how rejected promise can be asserted, an assertion needs to be provided with a promise instead of a function that returns it:
await expect(API.get("bad_url")).rejects.toThrowError();
You can mock the axios for promise rejection like this
jest.mock('axios', () => ({
post: jest.fn(() => Promise.reject(new Error(''))),
get: jest.fn(() => Promise.reject(new Error(''))),
put: jest.fn(() => Promise.reject(new Error(''))),
delete: jest.fn(() => Promise.reject(new Error(''))),
}));
This method worked perfectly for me.
Hope this works for you also.
I am trying to test this class method:
get () {
try {
return 'value'
} catch (e) {
return 'value2'
}
}
with:
test('the get method retrieves value', () => {
const value = MyClass.get()
expect(value).toBe('value')
})
My test coverage shows I have tested the try part of the method but not the catch part.
How can I cover the catch part?
You can test the catch part by wrapping the function call in a try catch block in your test AND expecting an assertion. Without expecting an assertion a run of the test that doesn't cause an error will still pass.
https://jestjs.io/docs/en/tutorial-async#error-handling
test('the fetch fails with an error', async done => {
expect.assertions(1);
try {
await fetchData();
} catch (e) {
expect(e).toMatch('error');
}
});
You can wrap the expectation inside setImmediate() or process.nextTick() as:
test('the fetch fails with an error', async => {
expect.assertions(1);
setImmediate(() => {
expect(fetchData).toMatch('error');
});
});
Please try this:
test('the fetch fails with an error', async () => {
expect(() => {
await fetchData();
}).toThrow();
});
More info: https://jestjs.io/docs/en/expect#tothrowerror