Ajax and Unit Testing Part Two, The Wrath of Mock

November 21, 2006

I've started the task of Unit Testing the MyBic Ajax / PHP Framework. I know I know I should have used Test Driven. Bad me. If you're unfamiliar with getting a Unit Test system created for testing JavaScript I suggest reading Part I "AJAX and Unit Testing, it's time to mingle" http://www.litfuel.net/plush/?postid=117

Once you have your JSUnit Framework set up you're ready to start testing your applications. For MyBic I really wanted to start testing the guts of what I send to the xmlhttprequest object as well as some of the help functionality of MyBic. Things such as the getForm method which converts a form ID into a nice string of escaped data, ready to send to the server. This way I can make sure all the parameters that are set by a user will reflect in the appropriate open, setrequestheaders, etc functions. You can find all my tests so far here: http://mybic.svn.sourceforge.net/viewvc/mybic/jstests/

One of the nice things about JavaScript is that you can mock out objects very easily. Here is an example of how I mocked out my getXMLHTTP method. This method is used to get the browsers XMLHTTPRequest() object. What I'm doing is creating a fake XMLHTTPRequest Object so I can inspect what I'm sending to it and how my application interacts with it.

  1.  
  2.  
  3. function stubXHR(responseText) {
  4.  
  5. window.XMLHTTP.prototype.getXMLHTTP = function() {
  6. ajaxObj.req = new Object();
  7. ajaxObj.req.open = function(method, url, async){
  8. testObj.method = method;
  9. testObj.url = url;
  10. testObj.async = async;
  11. }
  12. ajaxObj.req.readyState = 4;
  13. ajaxObj.req.status = 200;
  14. ajaxObj.req.queryString = '';
  15. testObj.headers = new Array();
  16. ajaxObj.req.setRequestHeader = function(headername,headervalue){
  17. //alert('in func: '+headername);
  18. testObj.headers[headername] = headervalue;
  19. }
  20. ajaxObj.readyStateFunction = false; // make sure the polling timer never gets set
  21. ajaxObj.req.responseText = responseText;
  22. ajaxObj.req.send = function(queryString) {
  23. ajaxObj.queryString = queryString;
  24. ajaxObj.responseHandler();
  25. }
  26. return true;
  27. }
  28. }


What I'm doing is creating a mock version of my getXMLHTTP method in MyBic. This way I can run tests and inspect what I send to the "open" method. What headers am I setting. Is my GET vs POST working correctly? Using the JS Prototype makes it very easy to mock out anything you want to inspect what you're getting sent. Notice how I mode out the ajaxObj.req.open method. This is the call that would send a request to the server. Well I don't want that. I just want to make sure that what I send to the XMLHTTPRequest object is accurate. In fact none of my tests send requests to the server. With MOCK objects you assume that the object your Mocking will work correctly. You're just trying to inspect what you send to it. For req.open I just want to store the method, url and asyn parameters into a testObj variable that I can inspect and verify when I sent a GET request that the URL will have the parameters listed properly.


Let's look at another example of some of the MyBic unit tests. This test (http://mybic.svn.sourceforge.net/viewvc/mybic/jstests/testOf_myBic_forms.html?view=markup) will test the getForm method of MyBic. This method is used as such:
var myFormString = ajaxObj.getForm('myformid');
MyBic will then loop through your form, collect all the form variables and create a nice little escaped string to send to the server. You don't have to touch a thing! Well how do we test that it actually works?

The way I decided to test it was to create a div called


So in my Unit Test file I'm going to fill that div with form elements. Select boxes, text boxes, text areas, etc... Then I'm going to use the getForm method and make sure each form element was captured correctly.


First off, let's take a look at our setup and teardown methods in our test script. These two methods are executed before and after EVERY test case.

  1.  
  2. function setUp()
  3. {
  4. ajaxObj = new XMLHTTP("mybic_server.php");
  5. testObj = new Object;
  6. }
  7.  
  8. function tearDown()
  9. {
  10. ajaxObj = null;
  11. testObj = null;
  12. document.getElementById('formstester').innerHTML = '';
  13. }


So what I'm doing is creating a fresh ajaxObj before each test, then clearing out our DIV's innerHTML after each test. This way I have a nice place to start my tests.

Let's take a look at one of the test cases

  1.  
  2. function testGetFormMethodWithOneTextField()
  3. {
  4. var formtest = '<form id="mybic_unittest"><input type="text" name="text_test" value="test123"/></form>';
  5. document.getElementById('formstester').innerHTML = formtest;
  6. var x = ajaxObj.getForm('mybic_unittest');
  7. assertEquals("One Test Field Failed", x, "&text_test=test123");
  8. }


If we read the function name we can see we're going to test our getForm method with just one text field. var formtest just contains a little form string with one text box element. We're going to stuff that string into our placeholding div at the bottom of our test file. Then we're going to call the MyBic getForm method and then just asset that what MyBic got and why MyBic should have got are in fact the same thing. If you look through that file you'll see more tests that aim to verify that the getForm method works as advertised.


So why Unit Tests? Piece of mind my friends. I now have thousands of developers relying on MyBic and I have to take the responsibility of not checking in changes that will break existing features. Unit Testing gives me the freedom to add more fixes and features and have the confidence to release proven code. I plan on getting full 100% code coverage in MyBic very soon and having nightly tests run against it.

I know most of you won't be Unit Testing Ajax frameworks so here are some tips I'd use:

Test your calling function - Each Ajax framework gives you a method or function to use to make an Ajax call to the server. Mock it out! Create a fake version of that function to use. You just want to make sure you're passing the right parameters to that function.

Test your callback function - You don't even need to mock anything out here. Just call your callback function and pass it different strings of data. Does your callback function handle bad data? Can you handle different types of data you may get?

Testing AJAX Applications is fairly easy in JavaScript. You just have to take the time to do it. If you really want to get fancy you can do full end-to-end integration tests with actual server calls to verify live data.

Now go start Unit Testing your AJAX Apps!




Comments

RSS feed for comments on this post.

  1. Shane says:
    January 5, 2007 @ 04:39 — Reply

    You should give xajax a test drive. Way less javascript code since xajax generates it for you :)

  2. Jim Plush says:
    January 5, 2007 @ 17:13 — Reply

    are you f'ing nuts? Thats why I created mybic in the first place. I do not want my php and javascript intermingled. Real programs need to real power of being able to do anything in both. xajax is definitely not a good solution for my needs.

  3. Anonymous says:
    February 8, 2007 @ 19:06 — Reply

    The AJAX applications could be easily tested with SWExplorerAutomation SWEA from http:\\webiussoft.com. SWEA records, replays and generates C# or VB.NET code for AJAX unit tests. SWEA doesn't require any changes to be done to the tested application.

Leave a Comment

Line and paragraph breaks automatic, HTML allowed: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <code> <em> <i> <strike> <strong>


Please enter the following word in the box: "june"