Solution

  • Let's create a spyOn function which takes two arguments:
    • obj - the object containing the method to be spied on
    • methodName - a string representing the name of a method
  • Let's initialize an array calls, which is used to store the arguments with which the spied method is called.
  • Create originMethod which will be the reference to the original method from the object. This is necessary to maintain the original functionality of the method while adding the spy capability.
  • Ensure that the specified methodName corresponds to a function in the object. If not, an error is thrown. This is a safeguard to prevent incorrect usage of the spyOn function.
  • The original method on the object is then overridden with a new function. This new function does two things:
    • It records the arguments passed to the method call in the calls array.
    • It calls the original method using apply to ensure that the context (this) and arguments are passed correctly.
  • Finally, the spyOn function returns an object containing the calls array. This allows the user of the function to inspect the arguments with which the spied method has been called.

Code

function spyOn(obj, methodName) {
  const calls = [];

  const originMethod = obj[methodName];

  if (typeof originMethod !== "function") {
    throw new Error("not function");
  }

  obj[methodName] = function (...args) {
    calls.push(args);
    return originMethod.apply(this, args);
  };

  return {
    calls,
  };
}