Javascript – Dynamically Built Front-End for Back-End Testing

When testing back-end systems have you always wished that your front-end would just handle itself so you can focus on what’s important? Well that’s what I’m discussing today.

Last week I had to write a HTML testing page for testing a back-end API which would be called by javascript. It seemed straightforward to follow this process below:

  1. Back-End: Read the API and code the Javascript AJAX call to that back end
  2. Front-End: Write HTML to create the input/output box interfaces and respective click functions to feed the Javascript calls
  3. Middle: Front-end Javascript calls the back-end Javascript to make the external API Calls

As I was stepping through this process I noticed that step 2 though simple was very tedious and killed a lot of screen space. The two key properties: simplicity and repetitiveness, are perfect cues calling for a dynamic/automated solution. I also had been inspired by PHP form generators on wordpress, so an automated form is what I set off to do.

Dynamic Solution

Pre-Requisites

  • Javascripted calls to your back-end, usually implemented as a service layer
  • jQuery

Goals

  • Front-end listing of all functions we want to test
  • Front-end form generation for all parameter inputs
  • Middle Glue that sends the dynamically built form parameters to the appropriate back-end call

Result

  • For every back-end java function you write, the front end will automatically list it
  • When you click a function in the list a form is are automatically generated
  • When you submit the appropriate function call is made to the back-end function with arguments passed in

It looks like this

Below is the 4 step process to create this tester page

1. HTML Body

Here is the HTML body we will be populating with our javascript

    <div id="functionList" style="float:left;min-width:200px;width:20%;">
        <h2>Test Functions</h2>

    </div>
    <div style="float:left;min-width:200px;width:20%;">
        <h2>Test Parameters</h2>
        <div id="parameterList"></div>
        <input id="fcnSubmit" type="submit"/>
        <br/><br/><strong>Last Arguments:</strong><p id="lastArgs"></p>
    </div>

    <div style="float:left;width:59%;">
        <table><tr><th><h2>Output: </h2></th><th><h2 id="fcnName"></h2></th></tr></table>
        <textarea id="output" style="width:80%;height:800px;"></textarea>
    </div>

2. Listing All Functions

First things first, we want our interface so we can choose which back-end function to test. This is pretty straightforward if you’ve bundled your service layer into an object. Here’s a sample where driveService is an object containing all our methods/functions that call our back-end API. Each method is listed out in an <a> element and plopped under the #functionList element, note that the var method variable in the for loop returns the name of the function. The attribute ‘method‘ added here plays a key role in that a .click modifier is added to it in step 3.

//Output Function List
for (var method in driveService) { //Fetches the function names as strings
$('#functionList').append(function () {
  var divItem = document.createElement("DIV");
  var aItem = document.createElement("A");
  aItem.id = method; //Function names put in id, will be extracted in next step
  aItem.setAttribute("class", "method"); //Set attribute class for use of a .click() function in next step
  aItem.href = "#";
  aItem.innerText = method;
  divItem.appendChild(aItem);
  return divItem;
});
}

3. Submission Form For Parameters

Now this step is only necessary if the function you want to test has parameters. Unlike funcitons, the parameter names are a bit nastier to extract as javascript doesn’t have a direct reference to them. However getting the parameter names can be done with this beautiful function from stackoverflow

function getParamNames(func) {
  var funStr = func.toString();
  return funStr.slice(funStr.indexOf('(') + 1, funStr.indexOf(')')).match(/([^\s,]+)/g);
}

Here we fetch our function name and use it to get the actual function. We then extract the parameter names and turn them into <input> elements as part of a <form>

//Output Parameter List
$('.method').click(function () {
  $('#parameterList input').remove(); //Cleanup arguments from last call

  //Pick up the function name from the 'id' field of the <a> elements created in the last step for use
  var methodName = $(this).attr('id');
  $('#fcnName').text(methodName);
  currentFunction = driveService[methodName]; //Here we use the name to extract the actual function not just the function name and store it in the global 'currentFunction'
  $('#parameterList h2').replaceWith('<h2>' + methodName + '</h2>');

  var parameter = getParamNames(currentFunction);
  if (parameter == null) { //In  case we have no args we can just use the function, output sent to #output
  $('#lastArgs').text('---');
  currentFunction().then(function (d) { $('#output').val(JSON.stringify(d)); });
}
else {
  var inputItem;
  for (var i = 0; i < parameter.length; i++) { //Output the array we got from getParamNames function
    inputItem = document.createElement("INPUT");
    inputItem.id = parameter[i];
    inputItem.placeholder = parameter[i];
    $('#parameterList').append(inputItem);
  }
}
});

4. Middle Glue for Submitting our Argument

Now that we’ve plucked our function and parameter names, fetched the actual function and put into a variable it’s time to extract the actual arguments passed in and forward them to the back-end function. We can pass in an array instead of the common comma seperated parameter list using .apply as seen in stackoverflow

//Call the function and output
$('#fcnSubmit').click(function () {
  var arguments = new Array();
  for (var i = 0; i < $('#parameterList input').length; i++) {
    arguments[i] = $('#parameterList input').eq(i).val(); //We conveniently access every input element we've put in as an array and pass the whole thing as a packaged argument array
  }
  $('#lastArgs').text(arguments);
  if (currentFunction != undefined) {
    currentFunction.apply(this, arguments).then(function (d) { $('#output').val(JSON.stringify(d)); });
  }
  else {
    $('#output').val('no function');
  }
});

Conclusion

And this is our solution for making every back-end java function have its name automatically listed, a form generated for it and having it submitted it to the appropriate function call. All with no hardcoding or evil evals, so all you need to do is focus on what is important: writing and testing those back-end functions that you originally meant to do. No need to manually tinker around and write those repetitive  HTML forms and wrapper functions to take input.

Hassle free softcoding: Write once, reuse many times. Enjoy =)

I’ve omitted the javscript for driveService layer since you can create your own javascript service layer to match whatever your backend is.
If you have any questions please feel free to contact me at colin.chung@ideanotion.net
~Colin

PS. if javascript fails to load it could be due to “cross-site origin resource sharing” but this is often a back-end issue

Tagged with: , , , , ,
Posted in JavaScript, Technical

Leave a Reply