0

I am using Jest for testing with Create React App and I am trying to test an asynchronous function along with a synchronous code and I need it to throw when the synchronous part has an error.

The test consist on expecting the function to throw when recieving wrong arguments types.

The function throw "Invalid arguments." when receiving arguments type other than "undefined" (function without arguments) or a "number".

The USER_API is the API url to invoke.

Here is the function:

export const getUsers = async (count, ...rest) => {
  if (["undefined", "number"].includes(typeof count) && rest.length === 0) {
    const response = await fetch(USERS_API);
    const users = await response.json();
    if (count && typeof count === "number") {
      return users.slice(0, count - 1);
    }
    return users;
  }
  throw "Invalid arguments.";
};

Here is the test:

it.only("should throw on invalid arguments", () => {
  const str = "hello";
  expect(() => getUsers(str)).toThrow(/invalid/gi);
});

I expexted the function to throw

But running the test shows: Expected the function to throw an error matching: /invalid/gi But it didn't throw anything.


Is the testing method right or am I writing a bad test? If is it bad how can I improve it?

Thank you.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Sofienne Lassoued
  • 418
  • 1
  • 5
  • 16
  • I don't get why you're writing this function that way. This approach of specifying the count upfront would be interesting if your count would limit the response length, with a query parameter for example. In your case you're just adding bloat to a simple fetch, it would be mch simpler and easier to read to separate the concern into two distinct functions (fetch and slice) – BJRINT Mar 26 '19 at 14:33
  • Possible duplicate of [How to assert an async method throwing Error using toThrow with Jest](https://stackoverflow.com/questions/54580327/how-to-assert-an-async-method-throwing-error-using-tothrow-with-jest) – Brian Adams Mar 27 '19 at 01:05
  • @BJRINT thank you for your reply, but I don't really know when to make tests inside the function or outside of it? For exemple please review this link: https://codeshare.io/GkyN3x – Sofienne Lassoued Mar 27 '19 at 09:34
  • @brian-lives-outdoors no my question is a combination between sync an async this is the trick! – Sofienne Lassoued Mar 27 '19 at 10:09
  • This is [a duplicate](https://stackoverflow.com/questions/54580327/how-to-assert-an-async-method-throwing-error-using-tothrow-with-jest). Change your function to throw an actual `Error` like this: `throw new Error('Invalid arguments.');` and your test becomes just `expect(getUsers(str)).rejects.toThrow(/invalid/gi);` – Brian Adams Mar 27 '19 at 11:10
  • @brian-lives-outdoors... Ah now I get it, you say that I must change my code and deal with it like only asynchrously. I understand... If I change it like that, in this case it will be a duplicate. – Sofienne Lassoued Mar 27 '19 at 11:45
  • Best practice is to throw an instance of `Error` and not a string, and you can do this: `expect((() => getUsers(str))()).rejects.toThrow(/invalid/gi);` if you **really** want to use an arrow function in the test, but it's not necessary – Brian Adams Mar 27 '19 at 11:49
  • @brian-lives-outdoors I understand, thank you! – Sofienne Lassoued Mar 27 '19 at 13:26
  • @Sofienne Lassoued Have you ever considered using a typechecker such as [Flow](https://flow.org/) or [Typescript](https://www.typescriptlang.org/), if you are really worried about type errors, this should be the best way to go imo ... You wouldn't even have to write test like these, as it will be verified at compile time for you. – BJRINT Mar 28 '19 at 11:05
  • @BJRINT Hello! Thank you for your reply, I tried using Angular once but I didn't use so much the types. – Sofienne Lassoued Mar 28 '19 at 12:01
  • @Sofienne Lassoued You can typecheck without using Angular, [you can use typescript with a react app easily](https://facebook.github.io/create-react-app/docs/adding-typescript), or if you'd prefer to only type annotate helpers functions Flow seems more adequate, [see this tutorial for example](https://flow.org/en/docs/react/events/) – BJRINT Mar 28 '19 at 12:53
  • @BJRINT thank you Flow looks good! – Sofienne Lassoued Mar 28 '19 at 16:01

1 Answers1

1

As your getUsers is an async function, it returns a Promise. So, in order to test it you need to do as follows:

it.only ( "should throw on invalid arguments", () => {
    const str = "hello";
    getUsers ( str ).then ( function ( success ) {

    }, function ( err ) {
        expect ( err ).toBe ( /invalid/gi );
    } );

One of the other ways to test asynchronous code is:

it.only ( "should throw on invalid arguments", () => {
    const str = "hello";
    try {
        await getUsers("hello");
    } catch (e) {
        expect(e).toMatch(/invalid/gi);
    }
});

You can get more details over here: Jest: Testing Asynchronous Code

Prerak Sola
  • 9,517
  • 7
  • 36
  • 67
  • Hello, It worked thank you, my bad I was waiting it to throw when passing a number (which is wrong) :p – Sofienne Lassoued Mar 27 '19 at 09:05
  • 1
    Again, **this is a [duplicate](https://stackoverflow.com/questions/54580327/how-to-assert-an-async-method-throwing-error-using-tothrow-with-jest)**. The function should throw an `Error` like this: `throw new Error('Invalid arguments.');`. Then the test is just: `expect(getUsers(str)).rejects.toThrow(/invalid/gi);` – Brian Adams Mar 27 '19 at 11:06