Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programatically fail a test #2129

Closed
excitement-engineer opened this issue Nov 18, 2016 · 20 comments
Closed

Programatically fail a test #2129

excitement-engineer opened this issue Nov 18, 2016 · 20 comments

Comments

@excitement-engineer
Copy link
Contributor

excitement-engineer commented Nov 18, 2016

In the jest docs it mentions an example in the async tutorial

// Or try-catch.
it('tests error with async/await', async () => {
  try {
    await user.getUserName(2);
  } catch (object) {
    expect(object.error).toEqual('User with 2 not found.');
  }
});

I think that this code should be written like this:

// Or try-catch.
it('tests error with async/await', async () => {
  try {
    await user.getUserName(2);
    fail();
  } catch (object) {
    expect(object.error).toEqual('User with 2 not found.');
  }
});

The fail() will prevent the test from passing if getUserName() does not throw an error.

However, I see no mention of the fail() command anywhere in the docs, is this a supported API?

@thymikee
Copy link
Collaborator

I'm not sure if we want to have a function like this.
fail() as you proposed here will also prevent the test from passing if getUserName() doesn't throw and returns instantly.

Instead you could test if getUserName function throws with e.g. .toThrow() method: http://facebook.github.io/jest/docs/api.html#tothrow

@excitement-engineer
Copy link
Contributor Author

How would you use toThrow() to check for a rejection of a promise? Isn't toThrow only used to check if errors are thrown in a particular function?

@thymikee
Copy link
Collaborator

If you want to test Promise rejection, you can still go on with something like this:

it('tests if getUserName rejects promise', () => {
  user.getUserName(2)
    .then(() => {}, error => {
      expect(error).toBe('My rejected Promise')
    })
});

There are plenty of ways, I just don't see the reason to add another one.

@excitement-engineer
Copy link
Contributor Author

The problem with your method is that if the promise resolves then the test will pass even though you expect it to reject.

The test should fail if the promise resolves. It should only pass if it rejects with the exact error that you expected.

it('tests error with async/await', async () => {
  try {
    await user.getUserName(2);  //If this resolves then the test will pass
  } catch (object) {
    expect(object.error).toEqual('User with 2 not found.');
  }
});

@thymikee
Copy link
Collaborator

I'm not sure if it's possible to do with async/await syntax, I didn't play with it much to be honest.

But maybe we could introduce a new matcher e.g. .toBeRejected(object | string)?

Then you could test the case like this:

it('rejects on username 2', () => {
  const object = {error: 'User with 2 not found'};
  expect(user.getUserName(2)).toBeRejected(object);
})

@excitement-engineer
Copy link
Contributor Author

Yeah such an API would be awesome! And you could also use snapshots here:

it('rejects on username 2', () => {
  expect(user.getUserName(2)).toBeRejectedWithSnapshot();

@thymikee
Copy link
Collaborator

Closing this in favour of #1377. You're welcome do discuss new this API shape there.

@KeKs0r
Copy link

KeKs0r commented Nov 30, 2017

Somehow this is not documented, but since Jest uses Jasmin this is possible:

test('How to manually fail tests', done => {
  done.fail(new Error('I want my test to fail'))
})

@foxyblocks
Copy link

You can also call done.fail() after done() has been called. This is important if you want to fail conditionally when a specific thing happens.

For example I wanted any call to console.error to trigger an exception and fail the test in question:

// console.error should fail the test
beforeEach(done => {
  jest.spyOn(global.console, 'error').mockImplementation(e => {
    done.fail(e);
  });
  done(); // it is important to call this here or every test will timeout
});

@ganikris86
Copy link

The above did not work for me. But managed to make it work using this,
test("handleResponse - reject", () => { return expect( new Promise((resolve, reject) => myFile.myMethod(input, resolve, reject) ) ).rejects.toEqual("promise rejection tested"); });

@davidnormo
Copy link
Contributor

In case anyone else comes across this, the global function fail works. I believe it's a feature of Jasmine.

it('should not fail', () => {
  try {
    myFunctionThatWillNotError()
  } catch (e) {
    fail('should not have thrown an error')
  }
})

@watsoncj
Copy link

This is the idiomatic example from the docs:

test('the fetch fails with an error', async () => {
  expect.assertions(1);
  try {
    await fetchData();
  } catch (e) {
    expect(e).toMatch('error');
  }
});

@javierfernandes
Copy link

javierfernandes commented Jan 4, 2019

This feature is also useful in case you are programmatically generating test/it blocks based on async code. For example reading a text file -avoiding readSync- to assert something for each line.

As the describe doesn't allow the callback to return a promise (#2235), you cannot generate many its. So the solution is to use a beforeAll for the async code. Then you can only have a single it/test for the whole file.
So, if an expect fails, the error won't be verbose enough to understand the error (which line failed)
It is then cool to have a way to make it fail with a custom message.

Here is my example. done.fail() worked but an out-of-the-box fail() would get rid of the done().
Also having a custom message for expect() could have done it.

    describe('examples from file', () => {
        let lines;

        beforeAll(() => new Promise(resolve => {
          lines = []
          // asynchronously read each line into lines
        }))

        it('should accept all lines in samples.txt', done => {
          lines.forEach(line => {
            if (!someBooleanChecking(line)) {
              done.fail(new Error(`"${line}" was NOT accepted !`))
            } else {
              done()
            }
          })
        })
      })

@jamesleech
Copy link

This is the idiomatic example from the docs:

test('the fetch fails with an error', async () => {
  expect.assertions(1);
  try {
    await fetchData();
  } catch (e) {
    expect(e).toMatch('error');
  }
});

Won't this pass if fetchData() doesn't throw as the test is expecting?

Therefore is something like this required?

test('the fetch fails with an error', async done => {
  expect.assertions(1);
  try {
    await fetchData();
  } catch (e) {
    expect(e).toMatch('error');
    done();
    return;
  }
  done.fail('expected promise rejection did not occur as expected');
});

@SimenB
Copy link
Member

SimenB commented Jan 14, 2019

Won't this pass if fetchData() doesn't throw as the test is expecting?

No, expect.assertions(1); will fail the test if no assertion is run

@SimenB
Copy link
Member

SimenB commented Jan 14, 2019

In case anyone else comes across this, the global function fail works. I believe it's a feature of Jasmine.

That will stop working at some point - it's not part of Jest's documented API.


Also, any reason why you aren't using this?

test('the fetch fails with an error', async () => {
  expect.assertions(1);
  await expect(fetchData()).rejects.toMatch('error');
});

@jamesleech
Copy link

I totally missed the expect.assertions(1) line. nice

@rmehlinger
Copy link

rmehlinger commented Apr 11, 2019

So related to this, I'm currently trying to force my Jest tests to fail if console.error or console.warn is called. Unfortunately, the only reliable way I've found to do so is to use fail():


console.error = function(message) {
  error.apply(console, arguments); // keep default behaviour
  fail(message);
};

const warn = console.warn;

console.warn = function(message) {
  warn.apply(console, arguments); // keep default behaviour
  fail(message);
};

Raising an error does not work reliably when dealing with asynchronous React code, unfortunately. Does anyone have any thoughts on how this could be made to work without resort to fail()?

It does look like using expect.assertions(Infinity) works, but that certainly seems like an abuse of the intent of expect.assertions.

@mail2tamilan
Copy link

mail2tamilan commented Nov 15, 2019

When we use it with restartBrowserBetweenTests:true -done.fail(msg) causing no such session error to the rest of the test cases when we perform it in chrome

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests