Jest test expecting specific error to be thrown [duplicate] - javascript

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')
})
});

Related

How to validate catch block of a function in Jest?

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");
});

Verify async function throws non-error object using jest

Using the jest framework, how can I verify that my async function throws something other than an Error?
In my examples below, the first one works as expected, as the function being tested throws an Error. The second example, where the function throws a string, doesnt work - jest doesnt verify that the function throws.
// Works as expected
test('verify error thrown', async () => {
const expected = new Error('actual-error');
const fn = async () => { throw expected; };
await expect(fn()).rejects.toThrow(expected);
});
// Fails with: Received function did not throw
test('verify non-error thrown', async () => {
const expected = 'non-error';
const fn = async () => { throw expected; };
await expect(fn()).rejects.toThrow(expected);
});
I don't think you need 'rejects' in either case. According to the docs it's not clear if the toThrow method expects results to be derived from Error. There is a regex form which might validate against a string.
rejects says that it unwraps the rejection so it can be matched, and that might give you the error message string from Error, or a string if the result was a string.
Dave's point was absolutely right; using rejects.toThrow was not the right way to go here.
Instead, I'm managed to verify the expected behaviour by simply catching the error and verifying it's value.
test('verify non-error thrown', async () => {
const expected = 'non-error';
const fn = async () => { throw expected; };
await fn().catch(error => expect(error).toStrictEqual(expected));
expect.assertions(1);
});
Note that expect.assertions(1) is important here. Without this, if the code being tested was updated to not throw, the test would still pass.

How to test that an error is re-thrown in a catch statement with Jest

I have a function that returns a promise, but I want to test that it has a catch defined, and then additionally test that it re-throws the error.
This is a very contrived example but it was the clearest way to show the issue. In my actual code, I am calling a function that is mocked to fail (vs the manually rejecting in this example), and I have additional logging in the catch statement, which explains the re-throwing of the error.
const foo = () => {
return new Promise((resolve, reject) => {
reject(new Error('reject')); // manually rejecting to mimic actual code...
}).catch(error => {
// do some additional logging...
throw error;
});
};
it('should catch and re-throw error', () => {
// Received function did not throw
// and
// Unhandled promise rejection
expect(() => foo()).toThrow();
// Test passes, even when `throw error` is commented out with false positive
expect(foo()).rejects.toThrow();
});
I can successfully check that the logging function is called, but can't figure out how to ensure the error is re-thrown after.
WORKING UPDATE :)
thanks to #skyboyer & #Bergi for getting me to think about the issue a bit differently, and exposing me to some of the finer points of jest
Below is both the updated code to show the logging function, and the updated tests i settled on.
The issues that led to this were
unable to test logging was called due to the error being re-thrown
unable to test the value of the error being re-thrown
Catching the rejected promise allowed me to do both.
I was going to leave in the rejects.toEqual test, but it seems redundant now...
interested in any feedback! and thanks again!
// myModule.js
export const logging = () => {};
export const bar = () => new Promise(resolve => {});
export const foo = () => {
return bar().catch(error => {
logging();
throw error;
});
};
describe('myModule', () => {
let fooReturn;
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(myModule, 'bar').mockImplementation(() => {
return Promise.reject({ error: 'bar error' });
});
jest.spyOn(myModule, 'logging').mockImplementation(() => {});
fooReturn = myModule.foo();
});
it('should catch and re-throw error', () => {
expect.assertions(1);
fooReturn.catch(result => expect(result).toEqual({ error: 'bar error' }));
// removed since the above test covers that the promise was rejected
// return fooReturn.rejects.toEqual(expect.anything());
});
it('should call the loggin method', async () => {
expect.assertions(1);
// prevents UnhandledPromiseRejectionWarning
fooReturn.catch(() => {});
expect(myModule.logging).toBeCalled();
});
});
You missed return.
https://jestjs.io/docs/asynchronous#resolves--rejects
Be sure to return the assertion—if you omit this return statement, your test will complete before the promise returned from fetchData is resolved and then() has a chance to execute the callback.
Your test should be
it('should catch and re-throw error', () => {
return expect(foo()).rejects.toEqual(expect.anything());
});
As u/Bergi noticed with async/await it may look more laconic:
it('should catch and re-throw error', async () => {
await expect(foo()).rejects.toEqual(expect.anything());
});
but if we miss to add await before our expect we will have exact the same issue as in version 1 without return. So beware.

Testing an asynchronous throw error in a Promise catch with Jest

I have the following code that I'd like to test.
const Component: React.FC = () => {
const handleSubmit = (action) => {
doSomethingAsynchronous()
.then(() => /* something on success */)
.catch((err) => {
// Display the error message
action();
// Rethrow the exception so it can be handled up the chain
throw err;
})
}
return <Form onSubmit={handleSubmit} />;
}
This code performs a simple asynchronous action that fails or resolves. On a failure, the component is re-rendered to show an error message, and the error is rethrown to log to the console/our logging system and for parent components to deal with.
The problem comes when I am attempting to test the error handling behaviour to ensure that the error messages are being set. Simple testing such as:
describe('Component', () => {
it('handles an error', async () => {
// Setup
const mockAction = jest.fn();
const render = shallowRender(<Component />);
submissionHandler = render.find(Component).invoke('onSubmit');
// Act
submissionHandler(mockAction);
await () => new Promise(setImmediate); // To wait for promise execution
// Assert
expect(mockAction).toHaveBeenCalled();
})
})
Results in Jest failing the test as an error has been thrown in the test by the component, inside the catch block (as expected). However, my attempts to suppress this also result in the same error being thrown and failing the test.
try {
// Act
submissionHandler(mockAction);
await () => new Promise(setImmediate); // To wait for promise execution
} catch (e) {}
I also tried using expects().toThrow(), but this instead returns the jest error Received function did not throw. I assume this is because due to the promise the execution is no longer in the same function scope, so isn't being recognised by Jest as originating from that function?
await expect(async () => {
submissionHandler(mockAction);
await () => new Promise(setImmediate);
}).toThrow();
Does anyone know the best way to test this? I'm aware I can cheat by making onSubmit return my promise here and catching the exception there, but I'd avoid doing that to stop my function returning for testing purposes.
You need to unpack your errors from your promise with .rejects
try this:
import { spyOn } from 'jest-mock';
...
it("should error", async() => {
spyOn(console, 'error'); #removes error from output
await expect( yourAsyncMethod() ).rejects.toThrow() # .rejects unpacks errors from promises
}

Chai: throwing error on async/await when no parameter is passed

I am trying to test my code (Typescript) and it should throw when no parameter is passed
getID(ID) { if(!ID){throw new Error('stop js')} ....}
it('should fail if no ID', async () => {
expect(async () => await myService.getID() ).to.throw("stop js");
})
Based on the documentation the above should work however when I run the test I get
1) myTest
should fail if no groupId is passed:
AssertionError: expected [Function] to throw an error
You are operating on Promises; async/await is just syntactic sugar for Promises as well.
When you run code like this:
it('should fail if no ID', () => {
expect(/* not async */ myService.getID()).to.throw("stop js");
});
...the call to getID will synchronously throw an Error. However, when you run code like this:
it('should fail if no ID', async () => {
expect(async () => await myService.getID()).to.throw("stop js");
});
...the call to async will pass a Promise into expect, which will asynchronously be rejected with your Error.
As mentioned by NineBerry in the comments, you can install and use the library chai-as-promised to operate on Promises:
return expect(async () => await myService.getID()).to.eventually.throw("stop js");
// or
return expect(async () => await myService.getID()).to.eventually.be.rejectedWith("stop js");
You will either need to return the result of expect, or await it; otherwise your test will not wait for the expect result before determining whether it succeeds.

Categories

Resources