timkindberg / jest-when Goto Github PK
View Code? Open in Web Editor NEWJest support for mock argument-matched return values.
License: MIT License
Jest support for mock argument-matched return values.
License: MIT License
Hey!
While using this lib with TypeORM, I'm facing this limitation for function overloading.
Let's say I want to mock a call for repository.findOne
method which have 3 overloads. I'll get this:
since jest-when only recognize the last overload of .findOne
which is findOne(conditions?: FindConditions<Entity>, options?: FindOneOptions<Entity>)
and I'm using the first one (which is findOne(id?: string|number|Date|ObjectID, options?: FindOneOptions<Entity>)
) ๐
Is there any know workaround for this? Meanwhile I'm using //@ts-expect-error
Great lib btw!
(node:16003) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1) at handledRejection
In my test, I mock rejected value without calledWith
, then I get the errors.
when(mockFn)
.mockRejectedValue({})
If I make it with calledWith
, it works:
when(mockFn)
.calledWith(expect.anything())
.mockRejectedValue({})
I get this error then I change jest-when
CODE from
this.mockResolvedValue = returnValue => this.mockReturnValue(Promise.resolve(returnValue))
this.mockRejectedValue = err => this.mockReturnValue(Promise.reject(err))
to
this.mockResolvedValue = returnValue => this.mockImplementation(() => Promise.resolve(returnValue))
this.mockRejectedValue = err => this.mockImplementation(() => Promise.reject(err))
It seems work. I think that because Promise computed too early. Is that right?
Could there be a workaround for having a partial argument signature matching? Let me illustrate this.
when(method)
.calledWith(
foobar,
foo,
bar,
baz,
qux,
quux,
quuz
)
.mockReturnValue('Yaaai!);
Instead of having this long calledWith
, to use the same concept as C# uses when testing with It.Is
to transform the above code into something like:
when(method)
.calledWithPartial( {
foobarArg: foobar,
quxArg: qux
}
)
.mockReturnValue('Yaaai!);
This way, you wouldn't need to worry about matching the exact signature over and over, only the variables that matter to you.
I just put the very first method name that popped into my head, but it can most definitely be improved for sure.
Maybe there's an easier way with jest.Any(constructor) but I'm not sure.
Could this thing be achievable?
Sample code:
const { when } = require('jest-when')
it('allows defining the default when NOT chaining', () => {
const fn = jest.fn()
when(fn).mockRejectedValue(false)
when(fn)
.calledWith(expect.anything())
.mockResolvedValue(true)
expect(fn()).rejects.toEqual(false)
});
Expectation: The test case to pass
When I set up the following:
when(get).expectCalledWith({ name: "test" }).mockResolvedValueOnce({});
I expect this to throw:
get({ name: "test", age: 21, height: 190 });
Are there any workarounds for this?
Had a bit of bother with using jest-when
this afternoon. Basically the following code triggers the issue:
const { when } = require('jest-when')
const testFn = jest.fn()
describe('testing', () => {
afterEach(() => {
testFn.mockReset()
})
test('test1', () => {
when(testFn)
.calledWith('test')
.mockReturnValueOnce(1)
.mockReturnValueOnce(2)
.mockReturnValueOnce(3)
expect(testFn('test')).toBe(1)
expect(testFn.mock.calls.length).toBe(1)
})
test('test2', () => {
when(testFn).calledWith('test').mockReturnValueOnce(1)
expect(testFn('test')).toBe(1)
expect(testFn.mock.calls.length).toBe(1)
})
})
So in the code above I setup two tests and run them with jest
. I set it so that the function I'll be mocking (a dummy function testFn
) is reset by .mockReset
in the afterEach
so I know setup from one test won't bleed into the other.
I setup test1
to have three values that get returned on calls but I only use one of them. After test1
has ran I expect test2
to not have anything associated with the mock. However if you run the above, rather than getting 1
as I expect I get 2
from test1
. This doesn't happen if I don't add further return values, then I get nothing (as expected).
I can get around this by adding the following line, however I shouldn't have to and this caused me a lot of problems tracking down errors this afternoon.
afterEach(() => {
testFn.mockReset()
testFn.__whenMock__ = undefined
})
I have this function
watchDataInformationRequests(
sortFn?: (a: DataInformationRequest, b: DataInformationRequest) => number
): Observable<DataInformationRequest[]> {
return this.store.select(RequestsState.dataInformationRequests(sortFn));
}
and I want to mock the store.select
when it receives a RequestsState.dataInformationRequests(sortFn)
.
I tried:
const fromNewestToOldest = (a: { createdAt: Date }, b: { createdAt: Date }) =>
b.createdAt.getTime() - a.createdAt.getTime();
when(store.select)
.calledWith(RequestsState.dataInformationRequests(fromNewestToOldest))
.mockReturnValue(of(sortedRequests));
when(store.select)
.calledWith(RequestsState.dataInformationRequests(expect.anything()))
.mockReturnValue(of(sortedRequests));
when(store.select)
.calledWith(expect.any(RequestsState.dataInformationRequests))
.mockReturnValue(of(sortedRequests));
when(store.select)
.calledWith(RequestsState.dataInformationRequests)
.mockReturnValue(of(sortedRequests));
In all the cases, when I call watchDataInformationRequests
the when is not recognized, and it returns null.
I am trying to do an integration test, so I cannot just mock watchDataInformationRequests
.
I tried mocking store.select
directly, and in that case it works, so I imagine that the problem comes from when
not being able to properly compare that the functions are the same, or I am providing the wrong value to when
.
How can I mock a function when it expects another function as argument?
Hi! ๐
I had a problem using createMock
from @golevelup/ts-jest
because it uses proxies to fake properties, and the test below against _isAllArgsFunctionMatcher
gives a false positive (returns a jest.fn() instance).
Here is the diff that solved my problem:
diff --git a/node_modules/jest-when/src/when.js b/node_modules/jest-when/src/when.js
index 760b9bb..b7590bc 100644
--- a/node_modules/jest-when/src/when.js
+++ b/node_modules/jest-when/src/when.js
@@ -93,7 +93,7 @@ class WhenMock {
let isMatch = false
- if (matchers && matchers[0] && matchers[0]._isAllArgsFunctionMatcher) {
+ if (matchers && matchers[0] && (typeof matchers[0] === 'function' || typeof matchers[0] === 'object') && '_isAllArgsFunctionMatcher' in matchers[0] && matchers[0]._isAllArgsFunctionMatcher) {
if (matchers.length > 1) throw new Error('When using when.allArgs, it must be the one and only matcher provided to calledWith. You have incorrectly provided other matchers along with when.allArgs.')
isMatch = checkArgumentMatchers(expectCall, [args])(true, matchers[0], 0)
} else {
I mock a module like so:
const { aMethodInTheModule } = require('./myModule');
jest.mock('./myModule', () => ({
aMethodInTheModule: jest.fn(),
}));
Then, the below code doesn't works:
when(aMethodInTheModule)
.calledWith('a')
.mockReturnValue(() => Promise.resolve('b'));
It works now, I'm just adding the tests and documentation for that in a PR.
Types are broken at the moment in @typed/jest-when
(as far as TypeScript is concerned, the output of jest.spyOn
is not a valid input for when
).
We need to document that it works, so that I could link documentation in a PR to DefinitelyTyped.
Given the following code:
import { jest } from "@jest/globals";
import type { SpyInstance } from "jest-mock";
import type { ExecSyncOptions } from "node:child_process";
import { isInGitRepository } from "../../src/helpers/git";
describe("tests", function () {
let execSyncMock: SpyInstance<string | Buffer, [command: string, options?: ExecSyncOptions | undefined]>;
beforeEach(function () {
when(execSyncMock)
.calledWith("git rev-parse --is-inside-work-tree", { stdio: "ignore" })
.mockImplementationOnce(function () {
throw new Error();
});
});
test("it returns false", function () {
expect(isInGitRepository()).toBe(false);
});
});
The TypeScript compilation fails with the following error:
Argument of type 'SpyInstance<string | Buffer, [command: string, options?: ExecSyncOptions | undefined]>' is not assignable to parameter of type '((command: string, options?: ExecSyncOptions | undefined) => unknown) | MockInstance<unknown, [command: string, options?: ExecSyncOptions | undefined]>'.
Type 'SpyInstance<string | Buffer, [command: string, options?: ExecSyncOptions | undefined]>' is not assignable to type 'MockInstance<unknown, [command: string, options?: ExecSyncOptions | undefined]>'.
The types of 'mock.results' are incompatible between these types.
Type 'MockFunctionResult[]' is not assignable to type 'MockResult<unknown>[]'.
Type 'MockFunctionResult' is not assignable to type 'MockResult<unknown>'.
Type 'MockFunctionResult' is not assignable to type 'MockResultReturn<unknown>'.
Types of property 'type' are incompatible.
Type 'MockFunctionResultType' is not assignable to type '"return"'.
Type '"throw"' is not assignable to type '"return"'.ts(2345)
Context:
I am trying to stub out calls to my database in my tests to return a specific object, this is what the code looks like:
const authToken = { /* ... */ };
when(db.getById)
.calledWith(`/users/${userId}/privateData/twitterToken`)
.mockResolvedValue(authToken);
However I'm finding this a bit too rigid, as in some cases I don't have the userId
since a fake user instance is used to authenticate. It requires me to do some weird manipulations in order to make the tests work. It would be neat if we could do something like this:
const authToken = { /* ... */ };
when(db.getById)
.calledWithRegEx(/privateData\/twitterToken/)
.mockResolvedValue(authToken);
In this way, any calls to db.getById
that matches my regex would receive the authToken
mocked value, without having to be too granular/specific.
This also applies really well when you have to provide a URL as a parameter, or some other long string literal that contains dynamic values.
Hey! Just found this library, very nice utility.
I'd love a way to set up a "default" mock using jest-when
. For example:
when(getUser).calledWith(expect.anything()).mockResolvedValue(null);
when(getUser).calledWith(1).mockResolvedValue(userFixture);
But this does not seem to work. The expect.anything()
does not seem to work. It works if I only have it, but not in combination with the "real" mock.
As a workaround, I have to specify the "incorrect" user ID that I use in my test, but I would love to not have to do that:
when(getUser).calledWith(666).mockResolvedValue(null);
when(getUser).calledWith(1).mockResolvedValue(userFixture);
When I mock one function in MyClass.prototype
, I would like to assert and expect that the mock was called with the correct this
value (on a particular instance).
AFAIU there is no possibility for that now โ but it would be a really nice functionality to have.
Or are there any ways to achieve that in the current version?
I have a use case where I'm generating a relatively large payload and I'd like when to make a snapshot for one of the arguments.
import { when } from 'jest-when'
const fn = jest.fn()
test('match snap', () => {
expect.hasAssertions()
when(fn)
.expectCalledWith(expect.toMatchSnapshot())
.mockReturnValueOnce('hi')
expect(fn({ bar: { a: 12 } })).toEqual('hi')
})
Basically I want to jest-when to look at the arg and if I pass the asymmetric snapshot matcher to use expect(arg).toMatchSnapshot()
instead of the normal .toEqual
What do you think?
For context, expect.toMatchSnapshot()
is only available in jest 23+, and even then expect.toMatchSnapshot()
doesn't actually work like you'd expect.
I am seeing several oddities with jest-when that I can't easily explain. Many of them surfaces, when I use expectCalledWith
.
For example:
This works
const { when } = require('jest-when');
test.only('test when', () => {
const fn = jest.fn()
when(fn).calledWith(1).mockReturnValue(1)
when(fn).calledWith(2).mockReturnValue(2)
when(fn).calledWith(3).mockReturnValue(3)
fn(1)
fn(2)
fn(3)
expect(fn).toBeCalledTimes(3)
})
This doesn't work, claiming that fn(2) expected 1
as a argument
const { when } = require('jest-when');
test.only('test when', () => {
const fn = jest.fn()
when(fn).expectCalledWith(1).mockReturnValue(1)
when(fn).expectCalledWith(2).mockReturnValue(2)
when(fn).expectCalledWith(3).mockReturnValue(3)
fn(1)
fn(2)
fn(3)
expect(fn).toBeCalledTimes(3)
})
What's more, I had a similar problem in my production code, where I had fetch module mocked and tried to describe it's behaviour. And it was working as expected only when I used calledWith
or when I used when(fetch).expectCalledWith
in the exact order of the calls (which is not desired, as a function call order in my case is interchangeable.)
**so this will work: **
notice the order of when.calledWith
arguments ("url3"
, "url2"
, "url1"
)
const { when } = require('jest-when');
test.only('test when fetch', async () => {
const fetch = require('node-fetch');
when(fetch)
.calledWith(expect.stringContaining(`url3`))
.mockReturnValueOnce(Promise.resolve(new Response(`3`)))
when(fetch)
.calledWith(expect.stringContaining(`url2`))
.mockReturnValueOnce(Promise.resolve(new Response(`2`)))
when(fetch)
.calledWith(expect.stringContaining(`url1`))
.mockReturnValueOnce(Promise.resolve(new Response(`1`)))
await (async () => {
let res1 = await fetch(`http://url1`)
expect(await res1.json()).toBe(1)
let res2 = await fetch(`http://url2`)
expect(await res2.json()).toBe(2)
let res3 = await fetch(`http://url3`)
expect(await res3.json()).toBe(3)
})()
expect(fetch).toBeCalledTimes(3)
})
and this will work:
notice the order of when.expectCalledWith
arguments ("url1"
, "url2"
, "url3"
)
const { when } = require('jest-when');
test.only('test when fetch', async () => {
const fetch = require('node-fetch');
when(fetch)
.expectCalledWith(expect.stringContaining(`url1`))
.mockReturnValueOnce(Promise.resolve(new Response(`1`)))
when(fetch)
.expectCalledWith(expect.stringContaining(`url2`))
.mockReturnValueOnce(Promise.resolve(new Response(`2`)))
when(fetch)
.expectCalledWith(expect.stringContaining(`url3`))
.mockReturnValueOnce(Promise.resolve(new Response(`3`)))
await (async () => {
let res1 = await fetch(`http://url1`)
expect(await res1.json()).toBe(1)
let res2 = await fetch(`http://url2`)
expect(await res2.json()).toBe(2)
let res3 = await fetch(`http://url3`)
expect(await res3.json()).toBe(3)
})()
expect(fetch).toBeCalledTimes(3)
})
but this will fail:
notice the order of when.expectCalledWith
arguments ("url3"
, "url2"
, "url1"
)
const { when } = require('jest-when');
test.only('test when fetch', async () => {
const fetch = require('node-fetch');
when(fetch)
.expectCalledWith(expect.stringContaining(`url3`))
.mockReturnValueOnce(Promise.resolve(new Response(`3`)))
when(fetch)
.expectCalledWith(expect.stringContaining(`url2`))
.mockReturnValueOnce(Promise.resolve(new Response(`2`)))
when(fetch)
.expectCalledWith(expect.stringContaining(`url1`))
.mockReturnValueOnce(Promise.resolve(new Response(`1`)))
await (async () => {
let res1 = await fetch(`http://url1`)
expect(await res1.json()).toBe(1)
let res2 = await fetch(`http://url2`)
expect(await res2.json()).toBe(2)
let res3 = await fetch(`http://url3`)
expect(await res3.json()).toBe(3)
})()
expect(fetch).toBeCalledTimes(3)
})
In both cases, I'd expect the expectCalledWith
to work the same way as calledWith
, except that expectCalledWith
would fail the test if given call did not occur. So I expect that all the above examples should work.
Am I missing some trick I should apply in those cases?
The fact that the sync and the async calls are behaving differently seems to me like a straight-forward bug.
The async call scenario is missing order-agnostic and order-aware expectCalled...
versions, so this from the outside looks like a feature I'd appreciate.
I reported both in here as they seem to be somewhat related - feel free to correct me, I can split it always into separate issues.
Currently jest-when use expect v24, unless user of library also use jest v24 whole swats of jest specific dependencies are duplicated.
I suggest that jest-when declare jest as peer dep with version like "jest": ">= 24"
Hey, after updating to v3.4.1, this code starts to fail
const { when } = require("jest-when");
it('works', () => {
expect.assertions(1);
expect(true).toBe(true);
});
with:
FAIL ./test.js
โ works (3 ms)
โ works
expect.assertions(1)
Expected one assertion to be called but received two assertion calls.
2 |
3 | it('works', () => {
> 4 | expect.assertions(1);
| ^
5 | expect(true).toBe(true);
6 | });
7 |
at Object.<anonymous> (test.js:4:12)
Test Suites: 1 failed, 1 total
After removing the require
the test passes.
It looks like this library will not work with Jest 28.
Cannot find module 'expect/build/jasmineUtils' from 'node_modules/jest-when/src/when.js'
Require stack:
node_modules/jest-when/src/when.js
src/functions/public/exposed-objects/contact-collection/get-enriched-contacts.spec.ts
at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/resolver.js:491:11)
at Object.<anonymous> (node_modules/jest-when/src/when.js:2:15)
Any plans to support that in the future or it's not gonna happen ?
Could there be a way to define the value that is always returned when no matches are made with calledWith
. So being able to set something other than undefined
:
const mock = jest.fn();
when(mock)
.mockReturnValue('default') // always returned
.calledWith('blah')
.mockReturnValue('test'); // returned when `'blah'` is passed
I'm a big fan of jest-when, and it makes using jest mocks far more pleasant than using them vanilla. I have a feature request (that I'd be very open to PR'ing, if you find it acceptable) to cover a particular usage that we've run into at my company.
Currently, calledWith
requires all function arguments to be specified. For the vast majority of use cases, I think this is sensible and correct. I think there are, however, a small set of times when it would be nice to be able to omit arguments in the stub configuration, because they truly do not matter for the subject under test.
Would you be open to adding an option to allow extra arguments to be ignored?
const myDouble = jest.fn()
// potential APIs
// new method
when(myDouble).partiallyCalledWith("foo").mockReturnValue("bar")
// options object in when
when(myDouble, { ignoreExtraArgs: true }).calledWith("foo").mockReturnValue("bar")
assert(myDouble("foo") === "bar")
assert(myDouble("foo", "other", "stuff") === "bar")
expect.anything()
or several could do the trick in most cases, at the expense of verbositymockImplementation
for these use caseswhen
and calledWith
wrapper and deal with the problem in userlandtestdouble.js, which has a very similar API to jest-when
and seems to me to come from the same stubbing lineage, has an ignoreExtraArgs
option during stub configuration.
sinon.js does partial stubbing by default in its calledWith
assertion. I think this is a bad default, but folks coming from Sinon would at least be familiar with this behavior.
(Edit) after a deeper issue search that I should've completed before hitting submit, I ran into #36. I think this request is related, but distinct, because I'm talking specifically about rare cases where trailing arguments have no meaning to the subject nor stub.
In this particular case, we're mocking out React function components. We have a collaborator component, and we want to verify that some child component is properly arranged with the correct props:
import * as React from 'react'
import { when } from 'jest-when'
import { render, screen } from '@testing-library/react'
import { SomeChild } from './SomeChild' // export const SomeChild = (props) => (<>{...}</>)
import { SomeParent } from './SomeParent'
jest.mock('./SomeChild')
describe('SomeParent component', () => {
it('should pass xyz prop down to SomeChild', () => {
when(ShomeChild).calledWith({ xyz: '123' }).mockReturnValue(<span>hello world</span>)
render(<SomeParent xyz="123" />)
expect(screen.getByText('hello world'))
})
})
You (or rather, I) would expect this to work! However, it does not. For legacy reasons that have nothing to do with the code under test, the React rendering system passes two arguments the the SomeChild
function, props
, and something else called refOrContext
. All of our function components are written to accept a single function parameter, props
, so the fact that React is passing in a second parameter doesn't do anything because it's always ignored.
We can fix the test by doing...
when(ShomeChild)
.calledWith({ xyz: '123' }, expect.anything())
.mockReturnValue(<span>hello world</span>)
...but it's a little more verbose and, more importantly to me, muddies how the test communicates its intent to the reader
Thanks for creating this wonderful package! Absolutely loving it!
Any plan on supporting ES6 Class constructor? Something like the following?
when(Car)
.constructWith('Mercedes')
.mockInstance({
color: 'red',
price: 50,
});
Just an idea! ๐
import {makeFoo} from "foo"
import {when} from "jest-when"
when(makeFoo)
.calledWith('bar')
.mockReturnValue(123)
abc.map(makeFoo) // mocking does not work
abc.map(x => makeFoo(x)) // mocking works
Any idea why the short form mocking does not work with 3.2 ?
Would it be possible to make it so that you could set or replace the default return value without also resetting previously-added training? Basically, could jest-when be modified to make the following test pass?
it('can add default after training, reduced', async () => {
const fn = jest.fn();
when(fn).calledWith(1).mockReturnValue('a');
fn.mockReturnValue('b');
expect(fn(1)).toEqual('a');
expect(fn(2)).toEqual('b');
});
I have used jest-when library to mock reading of a specific file like below
when(mockedReadFileSync).calledWith(path.resolve(__dirname, "./../../assets/settings.json"), { encoding: "utf-8" }).mockReturnValue(JSON.stringify(settingsFileContent));
It has been almost a week since I used the library in my project but is started throwing errors today. I've searched about it but only found some records about gulp.
Here is my task in vscode. When I run it, it gives me error below. Never fails when I comment out the only line of code that I use jest-when. Seemed like a version issue after I read the discussions about gulp but I am not sure. Any help is appreciated.
{
"type": "node",
"request": "launch",
"name": "Launch jest",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
},
"args": ["--detectOpenHandles", "--watch"],
"outputCapture": "std",
"autoAttachChildProcesses": true,
"outFiles": ["${workspaceFolder}/out/**/*.js"],
"preLaunchTask": "build"
}
RUNS src/__tests__/app.test.ts
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe[24464]: c:\ws\src\node_contextify.cc:1035: Assertion `args[0]->IsString()' failed.
1: 00007FF7003C3E0F napi_wrap+108911
2: 00007FF700367E16 v8::base::CPU::has_sse+61910
3: 00007FF700368191 v8::base::CPU::has_sse+62801
4: 00007FF70037297A node::OnFatalError+40330
5: 00007FF700BEDDCF v8::internal::Builtins::builtin_handle+321711
6: 00007FF700BED364 v8::internal::Builtins::builtin_handle+319044
7: 00007FF700BED657 v8::internal::Builtins::builtin_handle+319799
8: 00007FF700BED4A3 v8::internal::Builtins::builtin_handle+319363
9: 00007FF700CC9C8D v8::internal::SetupIsolateDelegate::SetupHeap+465453
10: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
11: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
12: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
13: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
14: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
15: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
16: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
17: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
18: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
19: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
20: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
21: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
22: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
23: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
24: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
25: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
26: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
27: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
28: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
29: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
30: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
31: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
32: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
33: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
34: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
35: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
36: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
37: 00007FF700C8F2C0 v8::internal::SetupIsolateDelegate::SetupHeap+225376
38: 00007FF700D0BE1E v8::internal::SetupIsolateDelegate::SetupHeap+736190
39: 00007FF700C8233D v8::internal::SetupIsolateDelegate::SetupHeap+172253
40: 00007FF700C5FC3C v8::internal::SetupIsolateDelegate::SetupHeap+31196
41: 00007FF700B2FB7F v8::internal::Execution::CallWasm+1839
42: 00007FF700B2FC8B v8::internal::Execution::CallWasm+2107
43: 00007FF700B306CA v8::internal::Execution::TryCall+378
44: 00007FF700B10CE5 v8::internal::MicrotaskQueue::RunMicrotasks+501
45: 00007FF700B10A40 v8::internal::MicrotaskQueue::PerformCheckpoint+32
46: 00007FF700BEDDCF v8::internal::Builtins::builtin_handle+321711
47: 00007FF700BED364 v8::internal::Builtins::builtin_handle+319044
48: 00007FF700BED657 v8::internal::Builtins::builtin_handle+319799
49: 00007FF700BED4A3 v8::internal::Builtins::builtin_handle+319363
50: 00007FF700CC9C8D v8::internal::SetupIsolateDelegate::SetupHeap+465453
51: 00007FF700C62092 v8::internal::SetupIsolateDelegate::SetupHeap+40498
52: 00007FF700C5FD4E v8::internal::SetupIsolateDelegate::SetupHeap+31470
53: 00007FF700C5F93C v8::internal::SetupIsolateDelegate::SetupHeap+30428
54: 00007FF700B2FAC1 v8::internal::Execution::CallWasm+1649
55: 00007FF700B2F32F v8::internal::Execution::Call+191
56: 00007FF700C1AEA7 v8::Function::Call+615
57: 00007FF7003F0C04 node::CallbackScope::~CallbackScope+868
58: 00007FF7003F0F7B node::CallbackScope::~CallbackScope+1755
59: 00007FF7003E8B74 v8::internal::compiler::Operator::EffectOutputCount+228
60: 00007FF700294F1F v8::internal::Isolate::isolate_root_bias+35119
61: 00007FF70040FFBE uv_process_kill+302
62: 00007FF700420CE9 uv_loop_init+1337
63: 00007FF700420E5A uv_run+202
64: 00007FF700322CD5 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfBucketsOffset+9365
65: 00007FF700396F37 node::Start+311
66: 00007FF7001F677C RC4_options+339580
67: 00007FF7011B874C v8::internal::compiler::RepresentationChanger::Uint32OverflowOperatorFor+152316
68: 00007FFE61057034 BaseThreadInitThunk+20
69: 00007FFE611A2651 RtlUserThreadStart+33
When configured to return a function, mockReturnValue
calls that function and returns its return value, rather than returning the function itself. This is different to the behaviour of mockReturnValue
on a plain jest.fn()
.
const returnValue = () => 'hello';
const mock1 = jest.fn();
mock1.mockReturnValue(returnValue);
expect(mock1('hi')).toBe(returnValue);
const mock2 = jest.fn();
when(mock2).calledWith('hi').mockReturnValue(returnValue);
expect(mock2('hi')).toBe(returnValue);
The first assertion succeeds but the second fails:
expect(received).toBe(expected) // Object.is equality
Expected: [Function returnValue]
Received: "hello"
Difference:
Comparing two different types of values. Expected function but received string.
The problem is here:
Line 72 in 25f0093
I think the simplest solution is to wrap the returnValue
in a function here:
Lines 86 to 87 in 25f0093
I like to follow the test pattern of given
, when
, then
. This pattern makes using a syntax using when
unintuitive in the given
block (which is where this library would be used).
I have created aliases for the supported functionality in a fork, which look like this:
given(..).calledWith(..).returns === when(..).calledWith(..).mockReturnValue
given(..).calledWith(..).onceReturns === when(..).calledWith(..).mockReturnValueOnce
given(..).calledWith(..).resolvesTo === when(..).calledWith(..).mockResolvedValue
given(..).calledWith(..).onceResolvesTo === when(..).calledWith(..).mockResolvedValueOnce
given(..).calledWith(..).rejectsWith === when(..).calledWith(..).mockRejectedValue
given(..).calledWith(..).onceRejectsWith === when(..).calledWith(..).mockRejectedValueOnce
given(..).calledWith(..).isImplementedAs === when(..).calledWith(..).mockImplementation
given(..).calledWith(..).onceIsImplementedAs === when(..).calledWith(..).mockImplementationOnce
This works with expectCalledWith
too, of course.
The exports WhenMock
, verifyAllWhenMocksCalled
and resetAllWhenMocks
are aliased to GivenMock
, verifyAllGivenMocksCalled
and resetAllGivenMocks
respectively.
It would also "fix" the linguistic problems #24 mentions by allowing given(..).returns(..)
.
In my implementation the two syntaxes can be combined in any way, which allows for calls like given(..).calledWith(..).mockReturnValue(..)
. However, it should be relatively simply to disallow this, and I'd be happy to do so.
Is this something you'd be interested in supporting?
When setting up mocks without any argument expectations, I get the following message:
Unintended use: Only use default value in combination with .calledWith(..), or use standard mocking without jest-when.
However, I would really rather have a single syntax for mock setups across the whole solution.
I understand that it does not play well linguistically with when
, but perhaps a more neutral alias can be exposed, like setupMock
, which would not produce this warning.
In fact, I am trying to migrate from a homemade solution where the function is named just that: setupMock
.
I've got a mock which needs to call a callback. Currently this is not possible with your library.
As a workaround I've got this:
myMock.mockImplementationOnce((...args) => {
expect(args).toEqual([
'putObject',
{
Bucket: 'attachments',
Key: 'some-guid',
Expires: 900,
ContentType: 'application/pdf',
},
expect.any(Function),
])
const cb = _.last(args)
cb(null, 'https://signed-url')
})
I have a function where the first argument is an identifier and the second argument is an options object. Usually I don't care about the options object, so I frequently load my default data using expect.anything()
for the second argument.
However for one test I need to load my data based on whether or not the options object contains some specific values. So I would like to be able to use expect.objectContaining
to match against the options object without making the test too brittle by matching against the whole thing.
After doing some testing, it appears that jest-when
doesn't currently support this, as expect.anything()
is matched even though expect.objectContaining()
would also match the call. I would prefer that jest-when
would deprioritize expect.anything()
over all other asymmetric matchers.
I'm running jest-when 3.5.2 and jest 27.5.1.
Here are the tests I wrote to nail down the issue:
it('supports expect.objectContaining()', async () => {
const fn = jest.fn();
when(fn).mockReturnValue(false);
when(fn)
.calledWith(
'id',
expect.objectContaining({
k: 'v',
})
)
.mockReturnValue(true);
// this test passes
expect(
fn('id', {
k: 'v',
})
).toBeTruthy();
});
it('deprioritizes expect.anything() against literal values', async () => {
const fn = jest.fn();
when(fn).calledWith('id', expect.anything()).mockReturnValue(false);
when(fn)
.calledWith('id', {
k: 'v',
})
.mockReturnValue(true);
// this test passes
expect(
fn('id', {
k: 'v',
})
).toBeTruthy();
});
it('deprioritizes expect.anything() against other asymmetric matchers', async () => {
const fn = jest.fn();
when(fn).calledWith('id', expect.anything()).mockReturnValue(false);
when(fn)
.calledWith(
'id',
expect.objectContaining({
k: 'v',
})
)
.mockReturnValue(true);
// this test fails
expect(
fn('id', {
k: 'v',
})
).toBeTruthy();
});
Hi, actually I'm receiving this warning when run my tests using react 16.9.0
console.log node_modules/jest-when/src/when.js:72
Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-async-component-lifecycle-hooks for details.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.
any idea how to fix it?
Seems like this usage of jest-when
is broke on version 3.3.0, and work using 3.2.0
const fn = jest.fn();
when(fn).calledWith(null).mockReturnValue('yay!');
expect(fn(null)).toBe('yay!');
the test is failing and i get this in the logs
TypeError: Cannot read property '_isAllArgsFunctionMatcher' of null
versions -
"jest-when": "^3.3.0",
"jest": "26.6.3"
Thanks
Just curious.
I'm not able to reset a mock and clear our my previous settings. I have to declare a new jest.fn()
in my beforeEach()
to get my jest-whens to reset.
It would help to have mockReset()
clear out the callMocks array.
It's a feature request. It would be nice to mock different return values for two (or more) different calls with the same arguments.
This could be a different method, e.g. calledWithOnce
/expectCalledWithOnce
.
I can submit a PR if you want
when(mock).calledWith(Symbol.for(`dummy`));
results in "Cannot convert a Symbol value to a string" error at runtime.
The reason is that you try to log debug the matcher using string literals but Symbols can not work in them.
The issue occurs here -
Line 19 in d89f087
Hi all,
I recently started using this library but I'm not getting it to work with a mocked class method that receives a callback in one paramater.
Here's an example code (I'm using ts-jest
here for the mocked()
):
import { when } from 'jest-when';
import ExtensionStorage from '../src/extension-storage';
jest.mock('../src/extension-storage');
it('test', (done) => {
const storage = new ExtensionStorage();
when(mocked(storage.getSettings))
.calledWith(expect.any(String), expect.anything)
.mockImplementation((key, callback) => {
callback('a');
});
storage.getSettings('', (val) => {
expect(val).not.toBeNull(); // <---
done();
});
});
Where ExtensionStorage.getSettings
is of type (key: string, callback: (settings: string | null) => void): void
. In this case, the highlight line is never called.
However, if I use the default way without jest-when
, it works:
const storage = new ExtensionStorage();
mocked(storage.getSettings).mockImplementation((key, callback) => callback('test'));
storage.getSettings('', (val) => {
expect(val).not.toBeNull(); // <---
done();
});
Any ideas? Thanks!
That would expect a Partial
of each of the arguments and apply expect.objectContaining
matcher internally to each of the passed arguments.
How it is useful
Imagine method A receiving an object { x: number, y: number, z: number }
as a parameter and delegating to method B which wants an {x: number, y: number }
. To avoid extra memory allocations (our app is sensitive to that), we pass the object as-is since it satisfies the reduced shape expected by method B.
However, .calledWith
fails because there is an extra unexpected property.
Workaround:
expect.objectContaining
matcher inside, but it has no type safety with Typescript.Is anyone else getting node-gyp errors related from the Bunyan dependency? I've read through various issues in the Bunyan and dtrace-provider repositories, but haven't found a concrete way around this that doesn't involve:
A) Installing python
B) Installing the windows essentials package
C) Manually editing the package-lock
Mainly just curious if anyone else is seeing this and if they have found a good workaround or if there is something we could change in the package.json to resolve the issue for everyone.
I use jest-when
quite a lot, and find it incredibly useful. However, I've struggled to get it to work when the calledWith
parameter is itself a mock, like in the following scenario:
import { mock } from 'jest-mock-extended';
import { mocked } from 'ts-jest/utils';
import { when } from 'jest-when';
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
import { LambdaDestination } from '@aws-cdk/aws-logs-destinations';
jest.mock('@aws-cdk/aws-logs-destinations');
test('jest-when matching on a mock', () => {
const mockLambdaDestination = mock<LambdaDestination>();
const mockLambda = mock<NodejsFunction>();
when(mocked(LambdaDestination)).calledWith(mockLambda).mockReturnValue(mockLambdaDestination);
const result = new LambdaDestination(mockLambda);
expect(result).toEqual(mockLambdaDestination);
});
The above test fails:
Error: expect(received).toEqual(expected) // deep equality
Expected: undefined
Received: {"bind": [Function bind]}
which indicates to me that jest-when
has not picked up that LambdaDestination
was called with the argument mockLambda
and hence isn't returning the value mockLambdaDestination
.
However if I re-write the test to not use jest-when
and instead check that LambdaDestination
was called with the expected mock value, it passes:
import { mock } from 'jest-mock-extended';
import { mocked } from 'ts-jest/utils';
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
import { LambdaDestination } from '@aws-cdk/aws-logs-destinations';
jest.mock('@aws-cdk/aws-logs-destinations');
test('jest matching on a mock without help from jest-when', () => {
const mockLambdaDestination = mock<LambdaDestination>();
const mockLambda = mock<NodejsFunction>();
mocked(LambdaDestination).mockReturnValue(mockLambdaDestination);
const result = new LambdaDestination(mockLambda);
expect(result).toEqual(mockLambdaDestination);
expect(LambdaDestination).toHaveBeenCalledWith(mockLambda);
});
So it appears that jest is able to work out that LambdaDestination
was called with mockLambda
, but that jest-when
is not able to.
These examples use mock
from jest-mock-extended and mocked
from ts-jest. I could try to come up with examples that don't use these libraries if that would help.
Take a look at this code:
import { when } from 'jest-when';
class MyName {
reGetName( name: string ) {
return name;
}
getName( name: string ) {
return this.reGetName( name );
}
}
it( 'test my name', () => {
const component = new MyName();
jest.spyOn( component, 'getName' );
when( component.getName )
.calledWith( 'mark' )
.mockReturnValue( 'mark' );
expect( component.getName( 'mark' ) ).toBe( 'mark' ); // Ok
expect( component.getName( 'john' ) ).toBe( 'john' ); // Error
} );
Once run this test it gives this error:
TypeError: this.reGetName is not a function
The problem is jest-when
change the default implementation of the function.
My temporary solution is reset the jest-when
implementation by doing something like this:
it( 'test my name 2', () => {
const component = new MyName();
jest.spyOn( component, 'getName' );
when( component.getName )
.calledWith( 'mark' )
.mockReturnValue( 'mark' )
.defaultImplementation( MyName.prototype.getName.bind( component ) );
expect( component.getName( 'john' ) ).toBe( 'john' ); // Ok
expect( component.getName( 'mark' ) ).toBe( 'mark' ); // Ok
} );
To solve this issue jest-when
should not change the function's implementation if it's not called with the added arguments.
Thanks for creating this!
I'm familiar with this technique in sinon, and I find that verifying arguments at the call site often results in better tests than verifying them after execution.
In trying to replicate the patterns I've used in sinon, it wasn't clear from the README how to write the default failing function that handles the wrong-args case.
I include my implementation below in hopes that you include it or something similar in your README:
// A default implementation that fails
const wrongArgs = (...args: any[]) => {
throw new Error(`Wrong args: ${JSON.stringify(args, null, 2)}`);
};
when(fn)
.mockImplementation(wrongArgs)
.calledWith(correctArgs)
.mockReturnValue(expectedValue);
Thanks for the amazing library! This is exactly what I have been looking for and I hope something like this gets added to the jest core codebase soon.
I have a lot of boilerplate code that I would like to reduce and I think with a small modification to this library I can make that happen. I often mock libraries that I use in my codebase to mock the return values from the library. For instance, in my code I might use an Azure sdk and write tests that will mock the return value of the SDK and test how my code behaves. It's possible to mock those classes easily with this library, but the code is a bit hard to read so cleaning it up would be nice.
current
when(jest.spyOn(MetricsAdvisorClient.prototype, 'listAnomalies'))
.calledWith(1)
.mockReturnValue(123);
when(jest.spyOn(MetricsAdvisorClient.prototype, 'listAnomalies'))
.calledWith(2)
.mockReturnValue(456);
Would you consider a PR for the when
function to accept a second argument so I can do something like this:
when(MetricsAdvisorClient.prototype, 'listAnomalies')
.calledWith(1)
.mockReturnValue(123);
when(MetricsAdvisorClient.prototype, 'listAnomalies')
.calledWith(2)
.mockReturnValue(456);
Internally, the when function would call jest.spyOn
when it sees 2 arguments and then return the when object as usual. I realize this example is trivial, but i sometimes have 3 different functions that im mocking and I think the code looks much cleaner with the second approach.
Another approach could be to export a new "when" function with a different name that calls jest.spyOn
internally.
Let me know what you think.
Keep up the great work!
it would be cool :)
const test = jest.fn((yes: boolean): string => (yes ? 'pizza' : 'pasta'));
when(test).calledWith(true).mockReturnValueOnce('Luigi');
console.log(test(true));
console.log(test(false));
// Actual:
// Luigi
// undefined
// Expected
// Luigi
// pasta
Rationale:
I have a factory in one place of a system that returns a method that is already mocked in certain way. What I was specifically looking for is a convenient way of adding another condition to this mock in a different part of the system.
I understand that I can rewrite my original test
declaration using jest-when, but the current behaviour of overriding existing implementations seems intrusive.
The syntax of when().calledWith()
itself implies behaviour that only happens under specific conditions.
Hi, after updating to the latest version our tests are failing on:
โ Test suite failed to run
Cannot find module 'expect/build/jasmineUtils' from 'node_modules/jest-when/src/when.js'
Require stack:
node_modules/jest-when/src/when.js
some.test.ts
at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:306:11)
at Object.<anonymous> (node_modules/jest-when/src/when.js:2:15)
If I understand it correctly it is related to #84 and expect
package.
In our project, we don't have any expect
package directly in node_modules
, only in subfolders.
$ npm ls expect
โโโฌ [email protected]
โ โโโฌ @jest/[email protected]
โ โโโฌ [email protected]
โ โ โโโฌ [email protected]
โ โ โโโ [email protected]
โ โโโฌ [email protected]
โ โ โโโฌ @jest/[email protected]
โ โ โโโ [email protected]
โ โโโฌ [email protected]
โ โโโ [email protected]
โโโฌ [email protected]
โโโฌ [email protected]
โโโ [email protected]
Any ideas on how to best fix it? I was not able to replicate this in a fresh project.
I guess one solution could be convincing npm to install one expect
version directly to node_modules.
Hey @timkindberg, I noticed that v3.2.0 seems to break existing tests and I'm not sure if that's expected. Based on semver I'd expect not.
You can see an example in these check results for tjenkinson/gh-action-auto-merge-dependency-updates#113.
It may take a while until the results are loaded so I'm also pasting an example here:
run โบ when the event name is pull_request โบ with an allowed actor โบ errors if the content type is incorrect
expect(received).rejects.toHaveProperty(path, value)
Expected path: "message"
- Expected value - 1
+ Received value + 11
- Unexpected repo content response
+ octokit/plugin-throttling error:
+ You must pass the onAbuseLimit and onRateLimit error handlers.
+ See https://github.com/octokit/rest.js#throttling
+
+ const octokit = new Octokit({
+ throttle: {
+ onAbuseLimit: (retryAfter, options) => {/* ... */},
+ onRateLimit: (retryAfter, options) => {/* ... */}
+ }
+ })
+
377 | Promise.resolve({ data: { type: 'unknown' } })
378 | );
> 379 | await expect(run()).rejects.toHaveProperty(
| ^
380 | 'message',
381 | 'Unexpected repo content response'
382 | );
at Object.args [as toHaveProperty] (../node_modules/expect/build/index.js:241:20)
at run.test.ts:379:41
at step (run.test.ts:52:23)
at Object.next (run.test.ts:33:53)
at run.test.ts:27:71
at Object.<anonymous>.__awaiter (run.test.ts:23:12)
at Object.<anonymous> (run.test.ts:375:57)
The related test code is https://github.com/tjenkinson/gh-action-auto-merge-dependency-updates/blob/56c26aed95cf78be3712376cb2d61b4eb041cfd3/src/run.test.ts#L347-L355.
When reverting 6190463 the tests are passing again. Do you have an idea what the issue might be? Thanks!
I'm still learning TypeScript, so maybe I'm just missing something, but how am I supposed to use mockResolvedValue
with a mocked Axios object? See example snippet below:
jest.mock('axios')
const mockedAxios = axios as jest.Mocked<typeof axios>;
// doesn't work
when(mockedAxios.get)
.calledWith('http://some-api')
.mockResolvedValue('foo'); // complains that "Argument of type '"foo"' is not assignable to parameter of type 'never'.ts(2345)"
// this seems to work w/ the generic type hint, but is super ugly...
when<Promise<unknown>, any[]>(mockedAxios.get)
.calledWith('http://some-api')
.mockResolvedValue('foo');
I think it might have something to do with when(mockedAxios.get)
resolving to WhenMock<unknown, [string, AxiosRequestConfig?]>
(T being unknown
specifically), but am kinda stumped.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.