Spies And Secret Agents In Javascript
Hello everybody,
Have you ever had a desire to become chief of secreat agent or spy? To be honest I never had such desire, but unwilignly I become chief of spies and secret agents which I created in javascript.
To put simply during unit testing of javascript sometime is needed to mock calling to some functions with replacement of that function, and sometime there is a need to mock that function without it's replacement.
The first scenario is much easier to implement, while the second one required usage of apply function.
Take a look at the following picture:
There are many js libraries which support both options ( with and without original function ). For example jasmine, sinonjs, etc. But I want to present code which display simple idea what is going behind the curtains.
<html> <head></head> <body> This content doesn't matter <script> var ClassForSpy = function(){ }; ClassForSpy.prototype.Function1 = function(){ console.log('Function1 executed'); return true; }; var InheritedClass = function(){}; InheritedClass.prototype = new ClassForSpy(); InheritedClass.prototype.Function2 = function(){ console.log('Function1 inside inherited class'); return 'Anything'; }; function CoolSpy(victim){ for(var key in victim) { victim[key] = CreateCoolSpy(key); } } function CoolSecretAgent(victim){ for(var key in victim) { victim[key] = CreateSecretAgent(key, victim, victim[key]); } } function CreateCoolSpy(key){ return function(){ console.log('CoolSpy for ' + key); } }; function CreateSecretAgent(key, context, originalFunction){ var sendToCenter = function() { console.log('I have send to command center secret info ' + key ); return originalFunction.apply(context, arguments); }; return sendToCenter; } console.log('Before spy section started') var inhClass = new InheritedClass(); inhClass.Function1(); inhClass.Function2(); console.log('--------------------Before spy section completed-------------------'); console.log('diletant spy or spy that substitutes original Function executed'); CoolSpy(inhClass); inhClass.Function1(); inhClass.Function2(); console.log('substitutes spy section ended'); console.log('Invisible spy section started'); var inhClass2 = new InheritedClass(); CoolSecretAgent(inhClass2); inhClass2.Function1(); inhClass2.Function2(); console.log('Invisible spy section ended'); </script> </body> </html>
If you copy/paste the following code, and look in console, you'll find following output there:
Before spy section started
Function1 executed
Function1 inside inherited class
--------------------Before spy section completed-------------------
diletant spy or spy that substitutes original Function executed
CoolSpy for Function1
CoolSpy for Function2
substitutes spy section ended
Invisible spy section started
I have send to command center secret info Function1
Function1 executed
I have send to command center secret info Function2
Function1 inside inherited class
Invisible spy section ended
Here we have two functions which imitate spies behaviour. The first one is CoolSpy and second one is CoolSecretAgent .
CoolSpy is interesting for cases if it is needed to substitute behaviour of some code, CoolSecretAgent can be used for monitoring of execution.