SiKaNrOnG.com
Agile Developer Extraordinaire
Blog
Rails Javascript Testing (unittest.js the right way)September - 02 - 2008 |
|---|
|
Hey all! It's been awhile since I've written. I hope you guys find this helpful, because I couldn't find this anywhere else on the internet and it took me ages. So, I've been using the javascript_test plugin. You can install this plugin with the following command in your project root directory: ruby script/plugin install javascript_test Now, what you'll find is by default all this does is set up a framework with which you can test your javascript libraries againt NEW markup, that YOU have to write. I found this highly unrealistic. Only VERY rarely is the javascript in a rails application going to be able to be accurately tested in a 'vaccuum', meaning that the DOM and the JS have everything to do with eachother. Also, you want to be able to test the effects of your rjs templates, most of which will have everything to do with your markup and your application server running. So, I thought about this for a while, and came up with (what I think) is a pretty elegant solution. When you run your tests (which you can do with the following command): rake test:javascripts What happens is the javascript_test plugin initializes a new WEBrick server running on port 4177, and all it really does is provide some basic functionality to run the JS tests with your newly written markup. It has nothing to do with your application ruby code, nor can you access that code via AJAX requests. Crap, huh? So, clearly this needs to be improved upon, and I believe I've found a way. Basically what I'm doing is opening a new window with my application server running in it, and simply running the tests (and the assertions) using the dom and JS included by the app! Completely unobtrusive, and you get the full power of the application when you test the JS. However... Issue: Same Origin PolicyBasically the first thing you're going to get screwed by is this, in all modern browsers, JS from within a parent window cannot access the DOM of the child window in any significant way. This is to prevent cross side scripting attacks, which would otherwise hijack your browser behind the scenes and post midget porn all over your facebook. Now, you wouldn't think it - but according to same-origin policy 'http://localhost:3000' and 'http://localhost:4711' are actually different domains (because of the port). Firefox 3 (and previous) don't even provide a way to temporarily disable this, so you have to exploit the single loophole in same-origin policy: subdomains. You're allowed to access the child window if it belongs to a subdomain of the parent window. So, "How do I turn localhost:3000 and localhost:4711 into jstest.mysite.test and mysite.test??" Solution: Subverting Same Origin Policy via ApacheThat's right! It is possible to use apache (with mod_proxy) and /etc/hosts to trick your browser into using the same origin subdomain loophole. I'm using apache 1.3 for this (Default on OSX Tiger), but it won't change too much for Leopard, or other versions of Apache in general. Step 1) Uncomment these lines in the file /etc/httpd/httpd.conf (default OSX tiger file location) #AddModule mod_proxy.c Step 2) Add these sections to httpd.conf (replace mysite with whatever you want to use) <VirtualHost *:80> Step 3) Add these two lines to /etc/hosts file 127.0.0.1 mysite.test Step 4) restart Apache: sudo apachectl restart Step 5) Edit the file vendor/plugins/javascript_test/lib/javascript_test.rb, change line 176 to browser.visit("http://js_test.mysite.test#{test}?resultsURL=http://js_test.mysite.test/results&t=" + ("%.6f" % Time.now.to_f)) Step 6) Make sure you add this to your application layouts AND to the <head> tags of the files in test/javascript (NOTE: DO NOT LET THIS BIT OF CODE ONTO YOUR PRODUCTION SERVER). I've created a mechanism that I can turn on and off in environment.rb, a better solution would be to use arguements passed to script/server on rails start. <script type="text/javascript"> Step 7) Write tests that use your server! This is an example from one of mine - it opens the application in a separate window and logs in before doing other tests. var mysite_window = null; Note that you don't have to have the window.open in the setup method, and in fact in most cases it's better if it just executes before the runner does (because it only needs to do so once). Anyway, You get the idea, now you can access all your JS methods from the window handler returned from window.open. So if I had a 'popup_cheesecake' function in my application JS, all I would have to do to test is is write something like: testCheesecake: function() { with(this){ ...And that's it! So anyway - I hope this has helped some people!! I know people on the rails scene have been looking into JS testing and seen solutions like the javascript_test plguin and shuddered in horror. This solution helps actually integrate the rails application AND the javascript testing suite, which in turn helps bring testing closer to what it's actually supposed to do - testing the software under real conditions! |









