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

Assert one mock called before another mock #4402

Closed
gavboulton opened this issue Aug 31, 2017 · 21 comments
Closed

Assert one mock called before another mock #4402

gavboulton opened this issue Aug 31, 2017 · 21 comments

Comments

@gavboulton
Copy link

Do you want to request a feature or report a bug?
Feature

What is the expected behavior?
I'd like to assert that one mock function is called before another similar to Sinon's calledbefore spy.

Is this a feature that's been considered/is being considered?

@cpojer
Copy link
Member

cpojer commented Aug 31, 2017

What would you like an API to look like for this in Jest?

@SimenB
Copy link
Member

SimenB commented Aug 31, 2017

I'd guess

const spy1 = jest.fn();
const spy2 = jest.fn();

spy1();
spy2();

expect(spy1).toHaveBeenCalledBefore(spy2);

How does one handle multiple invocations?

@gavboulton
Copy link
Author

@SimenB's API looks good to me.

Wouldn't multiple invocations be a separate thing? What's the use case for this?

@SimenB
Copy link
Member

SimenB commented Aug 31, 2017

const spy1 = jest.fn();
const spy2 = jest.fn();

spy1();
spy2();
spy1();

expect(spy1).toHaveBeenCalledBefore(spy2);

Might be a non-issue, but spy1 is called technically both before and after spy2

@henrycatalinismith
Copy link

Apologies for the bump! Just thought it might be worth registering the fact that I ended up on this issue as a result of a Google search cos I was half-wondering if there was a built-in way to accomplish this in Jest 😄

@SimenB
Copy link
Member

SimenB commented Oct 20, 2017

You can add a custom matcher for this using expect.extend.

If you do end up creating the matcher, please publish it and post about it on here. We can link to it in the docs

@SimenB SimenB closed this as completed Oct 20, 2017
@joscha
Copy link

joscha commented Oct 30, 2017

@SimenB can this really been done with extend? any call to a jest.fn() method would need to leave an array of timestamps or a call counter somewhere you can read from within the expect and I can't see anything that would qualify?

@SimenB
Copy link
Member

SimenB commented Oct 30, 2017

jestFn.mock.calls is an array that is pushed into on each calls, so you can assert order in the array.

https://facebook.github.io/jest/docs/en/mock-functions.html#mock-property

@joscha
Copy link

joscha commented Oct 30, 2017

jestFn.mock.calls is an array that is pushed into on each calls

That's per mock function, not overall, right? So I am unsure on how that would help in comparing whether one (jest.fn())() call was done before another.

@SimenB
Copy link
Member

SimenB commented Oct 30, 2017

Ah, good point. Brainfart. Not as easy to fix on our side either, then. Adding an extra field to the mockstate feels overkill.

Code is here, if anyone wants to tinker with it: https://github.com/facebook/jest/blob/b8e2fe384c17f3fb2abe8b0bc678d2ddbb11af1b/packages/jest-mock/src/index.js#L314-L315

(I still don't think we should add the matcher to jest, but we should make it possible to add in userland)

@mattphillips
Copy link
Contributor

Hey @gavboulton fancy seeing you here 😆 👋

@SimenB I've added the extra field for this in #4866 if you get a chance to take a look 😄

@SimenB
Copy link
Member

SimenB commented Nov 9, 2017

That PR has been merged. jest-community/jest-extended#98 can be tracked for the matcher

@SimenB SimenB closed this as completed Nov 9, 2017
@brigonzalez
Copy link

brigonzalez commented Mar 25, 2018

It appears that using Date.now() isn't precise enough to use toHaveBeenCalledBefore(mock) from jest-extended (jest-community/jest-extended#98).
I've been playing around with using process.hrtime() as the timestamp value instead. And I've been getting good results. I'd be happy to submit a PR if no one has any objections to this.

@SimenB
Copy link
Member

SimenB commented Mar 25, 2018

Yeah, we should probably use a global counter instead of a timestamp to match jasmine (https://github.com/jasmine/jasmine/blob/847a959b1380cd30b9133d85270ce9aebbcb690d/src/core/Spy.js#L39).

We're getting closer to a new major of Jest, so a PR removing timestamps from the mock state and adding a counter instead makes sense to me.

@brigonzalez
Copy link

brigonzalez commented Mar 25, 2018

Would this new counter be instantiated outside of ModuleMockerClass? I'm asking because I'm not sure of a way to assert call order between different mocks if the counter is instantiated inside of that class.

Edit: Ignore this comment. Not thinking straight. I may submit a PR for this new counter in a bit

@LukasBombach
Copy link

This is how I worked around it:

function setup() {
	const callOrder = [];
	const init = jest.fn().mockImplementation(() => callOrder.push('init'));
	const flush = jest.fn().mockImplementation(() => callOrder.push('flush'));
	// ... other stuff
	return { callOrder /* other stuff */ };
}

it('implements taboola', () => {
	const { callOrder } = setup();
	// ... other stuff
	expect(callOrder).toEqual(['init', 'flush']);
});

@daiict218
Copy link

I doubt the upper function is testing anything 😅

@clemenspeters
Copy link

clemenspeters commented Sep 24, 2019

My solution to this (where I want to make sure logout is called before login):

    const logoutSpy = jest.spyOn(client, 'logout');
    const loginSpy = jest.spyOn(client, 'login');
    // Run actual function to test
    await client.refreshToken();
    const logoutOrder = logoutSpy.mock.invocationCallOrder[0];
    const loginOrder = loginSpy.mock.invocationCallOrder[0];
    expect(logoutOrder).toBeLessThan(loginOrder);

@lucasMontenegro
Copy link

Here is a little example:

// grabSnake.js
export default function grabSnake (snake, headFirst, force) {
  if (headFirst) {
    snake.head(force)
    snake.tail(force)
  } else {
    snake.tail(force)
    snake.head(force)
  }
}
// ./__tests__/grabSnake.js
import grabSnake from "./grabSnake"
const spy = jest.fn()
const snake = {
  head (...args) {
    spy(`snake.head()`, args)
  },
  tail (...args) {
    spy(`snake.tail()`, args)
  },
}
describe(`./grabSnake`, () => {
  afterEach(() => spy.mockClear())
  test.each([[true], [false]])(`should grab in the right order (headFirst %j)`, headFirst => {
    grabSnake(snake, headFirst, 11)
    expect(spy.mock.calls).toMatchSnapshot()
  })
})

@dangreenisrael
Copy link

@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.
Projects
None yet
Development

No branches or pull requests