Skip to content

Instantly share code, notes, and snippets.

@nmestrada
Last active May 19, 2020 03:21
Show Gist options
  • Save nmestrada/223e8441864e939823f17700de00f906 to your computer and use it in GitHub Desktop.
Save nmestrada/223e8441864e939823f17700de00f906 to your computer and use it in GitHub Desktop.
Love is in the Air: ACT I

Act I: A Montague Spy in a Capulet Household

SLIDES

Romeo and Juliet begins as the Chorus introduces two feuding families of Verona: the Capulets and the Montagues. On a hot summer's day, the young men of each faction fight until the Prince of Verona intercedes and threatens to banish them. Soon after, the head of the Capulet family plans a feast. His goal is to introduce his daughter Juliet to a Count named Paris who seeks to marry Juliet.

Montague's son Romeo and his friends (Benvolio and Mercutio) hear of the party and resolve to go in disguise. Romeo hopes to see his beloved Rosaline at the party. Instead, while there, he meets Juliet and falls instantly in love with her.

Tybalt sees Romeo, a Montague, at this Capulet feast. Why is a Montague crashing a Capulet feast? Little does he know that Romeo is here not for vengeance but for LOVE 💕 .

Tybalt wants to write a spyOn function to see what Romeo is up to. 👀


Prompt

Testing and assertion libraries like Jasmine, Mocha, Chai, Sinon etc. have a feature called spies. Spies are funcitons which accept functions as arguments and return modified functions. While the returned function maintians all of the functionality of the original, when returned it is wrapped in new code which allows the spy to keep track of how the function is used: whether it is called, how many times, what arguments it is called with, what it returns, whether it throws errors, etc.

For this REACTO problem, 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 true if spy was ever called with val, else returns false
    • .returned(val): returns true if spy ever returned val, else returns false

Examples

function romeo(name) { 
  if(name === 'Juliet') return 'Romeo <3 Juliet';
  else return 'Meh'
}

const romeoSpy = spyOn( romeo );
romeoSpy('Juliet'); // 'Romeo <3 Juliet'

romeoSpy.getCallCount() ; // 1
romeoSpy('Rosalina'); // 'Meh'
romeoSpy(3, 5); // returns 'Meh'
romeoSpy.getCallCount(); // 2
romeoSpy.wasCalledWith('Rosalina'); // true
romeoSpy.wasCalledWith('Capulet'); // false
romeoSpy.wasCalledWith(5); //true
romeoSpy.returned('Meh'); // true
romeoSpy.returned('Capulet'); // false






Solution

The basic idea here is that we'll create a function to return which closes over some variables private to the scope of spyOn. We can then add some extra methods to that function before it is returned. (Remember that in JS, functions are first-class objects that can have properties and methods just like any other object.) This allows us to call what spyOn returns like a function (because... it is one), but also to use dot syntax to call methods available on the return value.

Here, we use Set to keep track of arguments and return values, but arrays or other structures might also be effective. This approach has the advantage of not using up memory for repeated values.

function spyOn (func) {
  let callCount = 0;
  const calledWith = new Set();
  const returnVals = new Set();

  function spy (...args) {
    const result = func(...args);
    callCount++;
    args.forEach(arg => calledWith.add(arg));
    returnVals.add(result);
    return result;
  }

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

  spy.wasCalledWith = function (argument) {
    return calledWith.has(argument);
  };

  spy.returned = function (result) {
    return returnVals.has(result);
  };

  return spy;
}

module.exports = spyOn;

Next Time on Love is in the Air REACTO:

Juliet's cousin Tybalt recognises the Montague boys and forces them to leave just as Romeo and Juliet discover one another.

Back to the main storyline

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment