R

E

A

C

T

O

xamples 

epeat

ode

pproach

ptimize

est

Make a Spy

Spies

Testing and assertion libraries include Jasmine, Mocha, Chai, Sinon, etc.

 

Many have a special feature called spies. Spies allow test specs to track how specific functions of interest are used:

  • whether they are called,
  • how many times they are called,
  • what they are called with,
  • what they return,
  • if they throw errors and more

Question

Implement a 'spyOn' function which does the following:

  • Takes a function, 'func', as its parameter
  • Returns a spy function, 'spy', that takes any number of arguments
  • 'spy' calls 'func' with the given arguments and returns what 'func' returns
  • 'spy' has the following methods:
    • .getCallCount() : returns the number of times 'spy' has been called
    • .wasCalledWith(val) : returns boolean of 'spy' ever being called with 'val'
    • .returned(val) : returns boolean of 'spy' ever returning 'val'

 Sample Output

function adder(n1, n2) { return n1 + n2; }

var adderSpy = spyOn( adder );

adderSpy.getCallCount(); // 0

adderSpy(2, 4); // returns 6
adderSpy.getCallCount(); // 1

adderSpy(3, 5); // returns 8
adderSpy.getCallCount(); // 2
adderSpy.wasCalledWith(2); // true
adderSpy.wasCalledWith(0); // false
adderSpy.returned(6); // true
adderSpy.returned(9); // false

Keep in mind:

  • not all functions take two arguments
  • .wasCalledWith and .returned do not need to be the most recent values

1. Create the SpyOn function 

var spyOn = function(func) {

    // keep track of function call count, start at 0
    var callCount = 0; 
    // array to store call results   
    var returnedVals = [];
    // array to store call arguments
    var calledWithVals = [];

    //function to be returned
    var spy = function () {
        /** code will go here **/
    };

    //create required methods on spy
    spy.getCallCount = function() {
        return callCount;
    };
    spy.wasCalledWith = function (val) { 
        // search through returnedVals array for val
    };
    spy.returned = function (val) { 
        // search through calledWithVals array for val 
    };

    return spy;
}

2. Figure out how to access function arguments


// Remember that function arguments are an array-like objects? 
// Turn arguments into an actual array  
//     Array.prototype.slice.call(arguments)


//Now we can use the resulting array to call .apply() on our function

var spy = function() {
    var args = [].slice.call(arguments);
    var retVal = func.apply(func, args);

    /* More code will go here */

};

3. Save the returned and called values in the proper arrays and increment call count

function spyOn (func) {
  let callCount = 0,
      calledWithVals  = [],
      returnedVals   = [];
    function spy () {
        let args = [].slice.call(arguments),
        returnVal = func.apply(this, args);    

    //store result of applying function
    returnedVals.push(returnVal);

    // add arguments array values to called values array
    calledWithVals = calledWithVals.concat(args);

    //increment function call count by 1
    callCount++;

    //return the result of the function call
    return returnVal;

  };

  spy.getCallCount;
  spy.wasCalledWith;
  spy.returned;

  return spy;
};

4. Implement the  'wasCalledWith' and 'returned' methods

// the 'indexOf' method of an Array returns '-1' if a value is not found
// we can use this to return a boolean value for the two search methods

 spy.getCallCount = function () { 
    return callCount; 
 };

 spy.wasCalledWith = function (val) { 
    return (callVals.indexOf(val) > -1); 
 };

 spy.returned = function (val) { 
    return (retVals.indexOf(val) > -1); 
 };

 return spy;

Final Solution

function spyOn (func) {
  let callCount = 0,
      calledWithVals  = [],
      returnedVals   = [];

  function spy () {
    let args = [].slice.call(arguments),
        returnVal = func.apply(this, args);
    returnedVals.push(returnVal);
    calledWithVals = calledWithVals.concat(args);
    callCount++;
    return returnVal;
  }

  spy.getCallCount = function () { 
    return callCount; 
  };

  spy.wasCalledWith = function (val) { 
    return (calledWithVals.indexOf(val) > -1); 
  };

  spy.returned = function (val) { 
    return (returnedVals.indexOf(val) > -1); 
  };

  return spy;
}

REPPPPLLLLLL

Made with Slides.com