My company makes heavy use of unit testing with JUnit for our backend code and also JWebUnit/HttpUnit for our client side code. As our JSPs began to get more JavaScript heavy, JWebUnit/HttpUnit began to reach the limits of its capabilities. To solve this problem, I started to look at existing JavaScript testing frameworks. I came across JsUnit but found that it was literally an analagous testing framework to JUnit, just for JavaScript. You could test methods but you could never fully test the flow of the application over the pages. What I really needed was something that tested the same way that HttpUnit tested but with complete javascript support. I wanted real world situations being tested in a way that would expose bugs in our client side code. The answer seemed fairly clear to me- Write an HttpUnit like testing suite written in JavaScript that runs in the same browsers that are supported by the application.
Notes About the Testing SolutionUse a library written in JavaScript that allows for HttpUnit tests to be written in a browser. The biggest problem is how do have two consecutive method calls that have the correct delay between them. In HttpUnit, when you call clickLink(), the next test method does not run until the next page is fully loaded. In JavaScript, when you want to click a link, the method will return immediately leaving you trying to test against a blank window with no contents (or even worse a window with the same contents since the browser hasn't unloaded the current page).
To get around this issue, I use a call stack where the next method only gets called when the library has determined the target page has loaded. Unfortunately, I could only seem to get this to work if I added a div tag to the end of all of our pages with a specific id. When that element is present, you know that the rest of the page has been loaded and can be manipulated. There is probably a way to do this with the onLoad event but in true XP style, I did what worked (and was easiest). If I (or anyone else) knows a cleaner way to do this, I would be more than happy to change that method. The call stack solves the main problem but creates some technical difficulties. Since not all methods are going to be included in a testing framework, people write their own that may be used for a single test. The problem is that when you add that method to the call stack you have to make sure that it is not going to call anything that would add itself to the call stack. This is because by the time your method runs, the entire call stack for the test has been defined so those calls will just add themselves to the end of the call stack. I briefly tried to have the executeMethod() call start its own sub stack but figured it was just as easy to make sure my methods didn't add themselves to the call stack.
Every method in the JsHttpTest.js file that is used for testing (e.g., clickLink(), setWorkingForm(), ...)
has a corresponding method (e.g., _clickLink(), _setWorkingForm(), respectively). The
versions of the methods that do not have a leading underscore add themselves to the call stack
whereas the methods with the leading underscore execute immediately. All the first set of methods
really does is add the '_' methods to the call stack. If you ever need these methods in
a custom validation function, you can just use the immediate execution version. The
sample test includes a custom validation function that makes calls to assert functions without
underscores. Since this uses the jsUnitCore.js library, pre-defined assertions (predefined by jsUnit)
run immediately and are not called by an underscored method. I may end up changing them
using something like:
_assertEquals = assertEquals; assertEquals = function() { _js_addToCallStack... }With the relevant logic to add the proper arguments. That would increase consistency but since this was originally just a tool used internally in my group, I haven't done it.
Another oddity that is caused by the page load/call stack is that when you click links, you need to know if the page is expecting to not change (for instance when a link just runs some JavaScript that alters something on the current page). To handle this case, the clickLink(), clickLinkWithImage(), and clickLinkWithText() take an optional second boolean parameter that indicates whether or not the page loaded marker should not be cleared. By default, all links are expected to change location but if you pass in 'false', the page loaded marker will stay and execution will continue expecting that the page has not changed.
The last big thing I can think of is onClick events. Right now, the links and form submits handle onClick events by executing the function before the link is clicked. Currently, if a link or submit button has an onClick, it will be processed but if a parent element has an onClick, that will be ignored. The other issue is that onClicks that refer to 'this' don't work properly so I replace the 'this' with a '_JS_CURRENT_WINDOW.document.getElementById()' call (if the element doesn't have an id, I generate a random id). Right now, this only replaces the first instance of 'this' so if a function has more than one, it probably won't work.
If you have any questions, just email me at agarza@vasoftware.com