-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
vi.fn()
performs an extra _await_ leading to mismatching behaviour when compared with normal function
#5747
Comments
The source of the issue seems to be in tinyspy: function I(e) {
R(u("function", e) || u("undefined", e), "cannot spy on a non-function value");
let t = function(...s) {
let r = A(t);
r.called = !0, r.callCount++, r.calls.push(s);
let m = r.next.shift();
if (m) {
r.results.push(m);
let [l, o] = m;
if (l === "ok")
return o;
throw o;
}
let p, d = "ok";
if (r.impl)
try {
new.target ? p = Reflect.construct(r.impl, s, new.target) : p = r.impl.apply(this, s), d = "ok";
// ^ It calls my function and puts its
// result in p without any extra wrapping
} catch (l) {
throw p = l, d = "error", r.results.push([d, l]), l;
}
let a = [d, p];
if (b(p)) {
let l = p.then((o) => a[1] = o).catch((o) => {
throw a[0] = "error", a[1] = o, o;
});
Object.assign(l, p), p = l;
// ^ It assigns l to p, l being p with extra .then.catch
}
return r.results.push(a), p;
};
i(t, "_isMockFunction", !0), i(t, "length", e ? e.length : 0), i(t, "name", e && e.name || "spy");
let n = A(t);
return n.reset(), n.impl = e, t;
} See my comments in the minified code |
Without the minified code, the source of the issue is the rewrapping of the Promise done by: if (isPromise(result)) {
const newPromise = result
.then((r: any) => (resultTuple[1] = r))
.catch((e: any) => {
// @ts-expect-error TS for some reasons narrows down the type
resultTuple[0] = 'error'
resultTuple[1] = e
throw e
})
// we need to reassign it because if it fails, the suite will fail
// see `async error` test in `test/index.test.ts`
Object.assign(newPromise, result)
result = newPromise
} Result is not anymore the original result but a value derived from it and paying the cost of an extra |
I'm attempting to come up with a fix (if feasible). |
Fix sent: tinylibs/tinyspy#45 |
Is it worth adding a dedicated test on Vitest's side once the fix gets released on tinyspy? If so I can work on adding such test |
Thanks you so much @sheremet-va, you are doing an awesome job! |
Describe the bug
Replacing
vi.fn(async () => {})
byasync () => {}
may lead to different results.In some cases, the ordering of asynchronous tasks might be key. In fast-check, we have a scheduler able to help our users to automatically detect potential race conditions leading to bugs in their code. In order to test it, we used to rely on Jest but recently we moved to Vitest to benefit from ES Module support.
While playing with
vi.fn()
I encountered some issues related to when promises resolved: the order order is not consistent with a version not relying onvi.fn()
.Reproduction
Here is an example highlighting the difference I encounter (diff between no
vi.fn()
or manual version of it andvi.fn()
):System Info
Used Package Manager
npm
Validations
The text was updated successfully, but these errors were encountered: