Click here to Skip to main content
15,867,453 members
Articles / DevOps / Unit Testing

Proxy Pattern in JavaScript

Rate me:
Please Sign up or sign in to vote.
4.75/5 (4 votes)
21 Nov 2013CPOL3 min read 36.1K   205   12   6
With one function call, wrap a class with a proxy in Javascript. A precursor to easy error mocking in node.js using mocha

Introduction

This is a very simple script to allow you to create a proxy for a class in JavaScript. Let's say we have a class (function that will be used as a constructor) Math which has a few functions defined on it such as add, subtract, multiply, and divide.

JavaScript
function Math() {
  this.IsMath = true;
}
Math.prototype = {
  add: function(a, b) {
    return a + b;
  },
  subtract: function(a, b) {
    return this.add(a, -b);
  },
  multiply: function(a, b) {
    return a * b;
  },
  divide: function(a, b) {
    return a / b;
  }
} 

We want to be able to modify its behavior in a way that will allow us to intercept each function call within this class with a beforeFunction event and an afterFunction event.

We can decide to do whatever we want with these two functions. You can visit this tip here which makes use of this generic proxy script to allow you to mock errors in mocha (node.js) very easily. For this article, I will define a simple proxy that logs function calls (this is done in node.js using console.log, but it can be modified easily for in browser code).

Using the Code

One function named createProxyDefinition is all you need. This function is defined in proxy.js which you can download from the files attached with this article. The function takes four parameters, prefix, beforeFunction, afterFunction, and more.

beforeFunction and afterFunction are pretty explanatory. Whenever a function is called of your target class, these two events will fire accordingly. The prefix is a unique name identifier for the proxy you want to create, this will allow you to "add" more than one proxy to the same class.

beforeFunction will be given three parameters, the first one is the function name being called, the second one is an array of arguments that is passed to the function and the third parameter is an array of the parameter names. If you return a value from your event, the original function will not be called and your return value will be used instead.

afterFunction will be given four parameters, the first one is the function name being called, the second one is the return value from the original function, the third one is the array of arguments that is passed to the function and the fourth parameter is an array of the parameter names.

The more is any extra object, it will extend the prototype of the class passed in.

An example works out best to explain how to use the class. I want to create a simple proxy definition that just prints out the function names being called along with their parameters, it can be useful for tracing function calls. Another more useful example, which is the reason why I wanted to write this article in the first place is to allow me to do full code coverage when writing node.js projects, please refer to the tip here.

To create a proxy definition that logs files (node.js module):

JavaScript
var beforeFunction = function(name, args, paramsArray) {
  if(!this.__levels) {
    this.__levels = 0;
  }
  for(var i = 0; i < this.__levels; i++) {
    process.stdout.write("\t");
  }
  console.log(" Entering: >> ", name, " : ", args);
  this.__levels++;
};
var afterFunction = function(name, ret, args, paramsArray) {
  this.__levels--;
  for(var i = 0; i < this.__levels; i++) {
    process.stdout.write("\t");
  }
  console.log(" Exiting: >> ", name)
}
var createCallsLogger = createProxyDefinition("callslogger", beforeFunction, afterFunction);

createCallsLogger is now a function that takes any constructor and modifies it as needed.

A sample usage for this would be:

JavaScript
createCallsLogger(Math);
var math = new Math();
math.add(1, 2);
math.subtract(10, 3);
math.multiply(5, 5);
math.divide(16, 4);

Now any function calls made on math will be logged to the console, a sample output follows:

usr/bin/node index.js
 Entering: >> add  :  { '0': 1, '1': 2 }
 Exiting:  >> add
 Entering: >>  subtract  :  { '0': 10, '1': 3 }
	 Entering: >> add  :  { '0': 10, '1': -3 }
	 Exiting:  >> add
 Exiting:  >> subtract
 Entering: >> multiply  :  { '0': 5, '1': 5 }
 Exiting:  >> multiply
 Entering: >> divide  :  { '0': 16, '1': 4 }
 Exiting:  >> divide
Process finished with exit code 0 

Under the Hood

The createProxyDefinition function takes a class definition (constructor function) and goes over all of its prototype functions, replaces each function with a new function that does the delegation of the calls and events needed. This function is in the attached file proxy.js. Roughly speaking, this is what it does in pseudo-code.

JavaScript
function __createProxy(constructor, proxyInstanceDefinition, defPrefix) 
{
  if (proxy already added)
    return;
  for each function in constructor.prototype
  {
      var newName = __ + functionname;
      copy original function reference to a new function with name "NewName"
      create a new function to replace the current. 
      The new function will call original function)      
  }
}

Points of Interest

This proxy creation method is not intended to be used in production code, ideally it is used for quickly mocking or tapping into other classes, like the case of using it to mock errors in node.js to achieve full coverage. The reason that you are not advised to use it in production code is because it basically creates new function instances for each function to be overridden, so ideally speaking, in a production environment, you should have custom written code to do such things, to achieve exactly what you want without the extra overhead of redirection and memory usage.

History

  • 21st November, 2013: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
Lebanon Lebanon
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralPraise Pin
Tareq Newaz Shahriar3-Nov-13 6:23
Tareq Newaz Shahriar3-Nov-13 6:23 
GeneralRe: Praise Pin
Ralph Varjabedian11-Nov-13 18:41
Ralph Varjabedian11-Nov-13 18:41 
Questionnice Pin
patelraj16-Oct-13 21:04
patelraj16-Oct-13 21:04 
AnswerRe: nice Pin
Ralph Varjabedian20-Nov-13 5:40
Ralph Varjabedian20-Nov-13 5:40 
Thank you.
Ralph Varjabedian
Chief Software specialist

My Blog
Bytesurge.com



QuestionProxy extract Pin
Robert Hoffmann16-Oct-13 5:43
Robert Hoffmann16-Oct-13 5:43 
AnswerRe: Proxy extract Pin
Ralph Varjabedian16-Oct-13 7:54
Ralph Varjabedian16-Oct-13 7:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.