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

test_runner: add support for chainable mockImplementationOnce #49088

Conversation

rluvaton
Copy link
Member

@rluvaton rluvaton commented Aug 9, 2023

This also includes appending implementation rather than replacing when using mockImplementationOnce

Fixes: #47718

this is a breaking change I believe

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/test_runner

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. test_runner Issues and PRs related to the test runner subsystem. labels Aug 9, 2023
@cjihrig
Copy link
Contributor

cjihrig commented Aug 9, 2023

Can you please run the linter. The current diff is very hard to review.

@rluvaton rluvaton force-pushed the add-support-for-chainable-mockImplementationOnce branch from 579caaa to f0e8da9 Compare August 9, 2023 21:41
@rluvaton
Copy link
Member Author

rluvaton commented Aug 9, 2023

Can you please run the linter. The current diff is very hard to review.

Yeah, this is why I set it to draft as WebStorm made some problems, this is now ready for review

@rluvaton rluvaton marked this pull request as ready for review August 9, 2023 21:42
@rluvaton rluvaton changed the title test_runner: add-support-for-chainable-mockImplementationOnce test_runner: add support for chainable mockImplementationOnce Aug 9, 2023
@rluvaton rluvaton force-pushed the add-support-for-chainable-mockImplementationOnce branch from 27c8cb3 to fdf1e08 Compare August 10, 2023 17:11
Copy link
Member

@atlowChemi atlowChemi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this PR should include docs updates @rluvaton

lib/internal/test_runner/mock/mock.js Outdated Show resolved Hide resolved
@atlowChemi atlowChemi added the semver-minor PRs that contain new features and should be released in the next minor version. label Aug 11, 2023
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/49088
description: this now return the context as well as appending when not providing the onCall argument.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: this now return the context as well as appending when not providing the onCall argument.
description: This now supports chaining calls.

Copy link
Member Author

@rluvaton rluvaton Aug 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to emphasize that it append the implementation as it wasn't before, and instead it just replaced it when mocking multiple times before calling the function

doc/api/test.md Outdated Show resolved Hide resolved
doc/api/test.md Outdated Show resolved Hide resolved
test/parallel/test-runner-mocking.js Show resolved Hide resolved
lib/internal/test_runner/mock/mock.js Outdated Show resolved Hide resolved
Comment on lines +71 to +77
const mocksSize = this.#mocks.size;
for (let i = 0; i < mocksSize; i++) {
if (!this.#mocks.has(i + nextCall)) {
this.#mocks.set(i + nextCall, implementation);
return this;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is right either (although I haven't tested this variation). I think you want a counter, and you want to loop over all of the existing entries in this.#mocks to see if each entry should be included in the counter or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution find the first empty implementation when you go incrementally, and then put it there

I don't see a problem with this and I would appreciate if you have a concrete example 😀

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change:

test('foo', (t) => {
  let cnt = 0;
  function addOne() {
    cnt++;
    return cnt;
  }

  const fn = t.mock.fn(addOne);

  fn.mock.mockImplementationOnce(() => 999, 0);
  fn.mock.mockImplementationOnce(() => 888, 1);
  fn.mock.mockImplementationOnce(() => 777);

  console.log(fn());
  console.log(fn());
  console.log(fn());
});

This currently prints 777, 888, 1. With the changes in this PR, it now prints 999, 888, 777 (note that I'm not even using chaining here).

Copy link
Member Author

@rluvaton rluvaton Aug 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is a breaking change this is why I wrote that in the PR description, what do you propose?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I didn't notice that you had updated the PR description recently.

I'm not sure if chaining can be implemented without a breaking change. I personally prefer the simplicity of mockImplementationOnce(() => 777) always meaning "mock the next call" to now needing to mentally keep track of which calls are mocked in order to support chaining.

Copy link
Member Author

@rluvaton rluvaton Aug 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can be implemented (only adding chaining) but I think it's gonna be unintuitive DX...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change:

test('foo', (t) => {
  let cnt = 0;
  function addOne() {
    cnt++;
    return cnt;
  }

  const fn = t.mock.fn(addOne);

  fn.mock.mockImplementationOnce(() => 999, 0);
  fn.mock.mockImplementationOnce(() => 888, 1);
  fn.mock.mockImplementationOnce(() => 777);

  console.log(fn());
  console.log(fn());
  console.log(fn());
});

This currently prints 777, 888, 1. With the changes in this PR, it now prints 999, 888, 777 (note that I'm not even using chaining here).

I got that it's a breaking change but this behavior doesn't look right.

I love the chaining possibility and I believe it's a great addition!

In the algorithm, shouldn't we just enqueue all mockImpl so we know the exact order?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do "enqueue" it by putting that in the next available location

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do "enqueue" it by putting that in the next available location

hmm, why is the order reversed on @cjihrig 's example? is it because of the second param?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to show the breaking change

@cjihrig
Copy link
Contributor

cjihrig commented Oct 21, 2023

#47718 was closed. I propose closing this as well.

@rluvaton
Copy link
Member Author

I think it's a valuable feature but closing for now...

@rluvaton rluvaton closed this Oct 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-ci PRs that need a full CI run. semver-minor PRs that contain new features and should be released in the next minor version. test_runner Issues and PRs related to the test runner subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

test_runner: mock.mockImplementationOnce only works for the last call
5 participants