AniUI Academy

Spy on an existing object method

hardFunctions & Closures+350 XPPractice

This is what jest.spyOn does. You replace a method on an object you do not own, record what it is called with while letting it behave exactly as before, and restore it when the test ends so nothing leaks into the next one. The this handling is where naive attempts break: the replacement is called as a method, so it must forward that receiver to the original or the original loses access to the object's own state.

Implement spyOn(object, methodName). It replaces object[methodName] with a wrapper that records the call and then invokes the original method with the same this and arguments, returning the original's result unchanged. It returns a spy handle with calls, an array holding one array of arguments per recorded call in order; callCount, the number of recorded calls, starting at 0; and restore(), which puts the original function back so that object[methodName] is once again the exact same function reference it was before, after which calls are no longer recorded. Spying on two different methods of the same object gives two independent handles.

Arguments

objectobject
The object whose method should be wrapped in place.
methodNamestring
The key of the method to spy on.

Returns

{ calls: any[][]; callCount: number; restore(): void } — A spy handle exposing a call log and a restore() method that puts the original function back.

Examples

const api = { total: 0, add(n) { this.total += n; return this.total; } };
const spy = spyOn(api, "add");

api.add(2);        // 2 — original behaviour preserved
api.add(3);        // 5
spy.calls;         // [[2], [3]]
spy.callCount;     // 2
spy.restore();     // api.add is the original function again
const user = { name: "Ada", greet(g) { return g + ", " + this.name; } };
const spy = spyOn(user, "greet");

user.greet("Hi"); // "Hi, Ada" — `this` still reaches the method
spy.calls;        // [["Hi"]]

Forwarding this from the wrapper is what keeps methods that use the receiver working.

What it has to do

  • The wrapper returns the original method's result, so behaviour is unchanged.
  • Invoke the original with the object as this, so methods using this still work.
  • Record one array of arguments per call in calls, and keep callCount in step.
  • restore() puts back the identical original function reference.
  • Calls made after restore() are not recorded, and two spies on one object stay independent.

Notes

  • Call the original with original.apply(this, args) from inside the wrapper — using the wrapper's own this is what forwards the receiver.
  • Save the original function reference *before* overwriting it. restore() just assigns it back; a fresh reference would break identity checks.

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +350 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.