Syntax Highlighting

Saturday, August 20, 2011

Jasmine and Javascript and AJAX

I just came into a very frustrating problem about testing Javascript AJAX calls with Jasmine. The basic upshot is that IT DOESN"T WORK as of this writing.

I found that many people answered peoples' questions on this subject with irrelevant answers, like how to Mock an AJAX request. Usually on StackOverflow.

Quite personally, I hate Mocking as it doesn't really serve too much of a purpose if you are developing both ends as the Mock has to change if the server request/response changes. This is the case where the Mock just mimics the specification of the code. Useless in this sense, because you spend too much time specifying the test just to mimic the code. If you have to change the test every time you change the code, you're doing the wrong thing. I'm not a big fan of test driven development, but these days it's the only way to figure out how things DON'T work. That is because nobody took the time to implement correctly, THINK about the edge cases, and fully explore the problem Which is something they SHOULD have taught in school these days. I apologize,  I digress. Let's get to the problem at hand.

For my particular problem I wanted to see if an object called API could get to the server and load some XML from the response.

    get : function(cb) {
        var http = new XMLHttpRequest();
        http.onreadystatechange = this._onAPIResponse(http, cb);
        http.open("GET", this.hostUrl + "apis/get_api.xml", true);
        http.send(); 
    },
    _onAPIResponse : function(http, cb) {
       var callback = function (event) {
          if (http.readyState == 4) {
            if (http.status == 200) {
                var apiXML = http.responseXML;
                this._loadAPIMap(apiXML);
                cb(this,true);
            } else {
                cb(this,false);
            };
          };
        };
        return callback.bind(this);
    },
    
You can get a Jasmine spec to issue an AJAX request and get to the server okay. However, on the client side, the response gets lost somewhere. So, therefore, testing Async Javascript calls in Jasmine is pretty useless when using XMLHttpRequest.

This is what happens. The following contains a Jasmine spec for testing the API.get() function to see if it can get to the server. We use a real callback function and use the Jasmine runs() and waitsFor() functions to handle the asynchronous nature of the calls in Jasmine.


    it('should be able to login', function () {
        var called = false;
        var callback = function (api, result) {
            called = true;
        };
        runs( function() {
            api.get(callback);
        });
        waitsFor(function() {
            return called;
        });
        runs( function () {
            expect(api.isLoaded()).toEqual(true);
        });
    });

The following is supposed to happen is when the AJAX call is made. If the request finishes (readyState == 4) returns a OK status (status == 200) it loads the XML and the API.isLoaded() function returns true. The callback function is there for notification.

Using the Jasmine test above, with a couple of clever alerts or console logs, this is what I see happens.

1. The Jasmine spec calls api.get().
2. api.get() makes the AJAX request.
3. The server gets the request.
4. The server responds with the proper XML and returns a status of 200 OK.
5. The callback gets called with (readyState == 2, status == 0, responseXML == null).
6. The callback gets called again with (readyState == 4, status == 0, responseXML == null).
7. Polar goes WTF?

Thinking things were wrong with cookies, rails protect_from_forgery, and such, I researched and eliminated all causes, even with a peek into the stream with Wireshark.

Now, in order to confirm for my own sanity that the AJAX request was actually working, I set up a small HTML file for it to respond with a script containing:
        function callback(api, result) {
            alert("Result " + result);
        };
        var api = new BusPassAPI("http://localhost:3000/");
        api.get(callback);


I definitely can say that the AJAX call works and gets the proper answer and loads the XML correctly.

So, there is something a miss in Jasmine. So, what I hoped to accomplish seems like it needs yet another framework to test. I neither have the time nor inclination to find out what is wrong here.

The problem is that I want to test the integration of the Javascript with the server, because that's where it is going to break. Not in the Javascript programming and testing to see if I can actually program correctly. However, there is always a mismatch of what might happen if two separate entities don't play with each other nicely. So, I'm more interested in the "integration testing".

Unfortunately, the frameworks that work with Rails seem like an horrendously tedious amount of stuff about manipulating web pages, specifying clicks, and such, when all I want to do is see how a particular function work with the server at hand. I haven't found that answer yet.

Monday, August 15, 2011

Hit by Eclipse WTP Test Plugin

Today, I had a unpleasant, let's say shocking, surprise. I installed the WTP test plugins to Eclipse. I thought this largely harmless. I was playing around with some Javascript and OpenLayers-2.0. and wanted to see if I could get some tests for Javascript working. I bummed around until I found WTP Test plugins for Eclipse. It installed a menu item in the top menu bar entitled "WTP Tests". It has one menu item "Open".

I decided to see what this "Open" did, it brought up a dialog in which I selected some tests. I'm not exactly sure what I selected, but it "reconfigured" my workspace. That is, it deleted everything I was working on, except for a few old project directories I didn't need.

WTF?

I mean, why not all, FFS? How did it decide that it only wanted to delete stuff I cared about? And, it installed a project called "testWebExport".

Man, that's the last time I trust this piece of software. I suggest keeping everything you own out of Eclipse's workspace directory. Mine was "$HOME/workspace".

My chief suggestion is that you keep your project source code in another directory, and just let Eclipse manage the project metadata it's "workspace". Consider the workspace owned by Eclipse. If you don't want to loose your stuff, keep it in some place *you* own.

Luckily, I have most stuff backed up into Github, but I did loose a branch and a few deltas, because I didn't upload them yet. Piss me off!

Thanks Eclipse WTP Tests for making me start all over. Geez.