September 1, 2007

rails - save! broke the contract - mocking to rescue

Posted by Sudhindra Rao

Detail descriptions of mockist and classicist are on Martin Fowler's blog.


Consider

   class Person < ActiveRecord::Base
       #has name, address, home, business, cell phone numbers
      .... more object behaviour
   end


The intended behaviour is to do some system specific tasks incognito - lets say send an email to all his friends.

So we alias the save method as follows

   alias :save_with_notification, :save


   def save_with_notifications
      send_emails
      save
   end

This works well for save. But when somewhere we call save! it all breaks - i.e. our save_with_notifications is not called and nobody gets notified.

The root cause(as analysed by David Vollbracht (not this one)) of this problem was this piece of rails code

   def save
      create_or_update
   end

   # Attempts to save the record, but instead of just returning

   # false if it couldn't happen, it raises a

   # RecordNotSaved exception

   def save!

      create_or_update || raise(RecordNotSaved)

   end


save! does not call save.

On further investigation I found functional tests around the base class for active_record.

But what was missed was a good unit test with mocking like so

   test "save! should successfully save object by calling save" do
      base = Base.new
      base.expects(:save).returns(true)
      assert_nothing_raised base.save!
   end

   test "save! should throw on exception if save fails" do
      base = Base.new
      base.expects(:save).returns(false)
      assert_raises RecordNotSaved, base.save!
   end

Here is a place where mocking does a much better job at testing a specific contract that is a convention and was missed due to black box testing.

This is not the only way to fix this problem though.

The real problem is that the requirements did not capture the fact of aliasing save or save! methods.

Testing this scenario functionally is perfectly possible though the testing could get more involving.

The tests in that case would make sure that aliasing of save method does not break save!.

But the above mocked tests are as simple as it gets and they reflect the simpler(obvious?) underlying requirement of save! calling save.

Note: Formatting code on blogger sucks

May 9, 2007

:partials are objects - too..

Posted by Sudhindra Rao

My recent adventures with ruby and rails have been on an actual application that will be used by 1000s of users. This application seems to suit perfectly for the design of rails. The good thing is that there are not many database transactions, mostly the users are expected to read and search for stuff on this website.

Many parts of this application are reusable. What changes most of the times is the way the parts look like when they are appear in different parts of the website. Also some times the details of those parts behave in different ways on different views(ajax/non-ajax, popup/tab-module, associated/unassociated, etc.).

This behaviour actually make them perfect candidates to be partials so that they can be programmed once and then the view can overlay and change what and how it shows up.


There are some things that really impress me about ruby - especially the part where everything is an object and the duck typing. Also having started doing real object oriented programming since I joined ThoughtWorks ( the other way of doing "OOP" is where I have a Universe class which knows about everything - that was so easy to do in those days), my mindset of trying to find objects has changed the way I write software.


With ruby - when used correctly - all these concepts fit perfectly. Ruby by design allows you excellent encapsulation - a very important feature of OO Design( eg. the duck typing - u only need to know whether it quacks like a duck - no internals are exposed when responding to a message). Rails tries to take these constructs in ruby one level further to the web application level, where the views also can be treated as objects(another important layer that can be treated like an object is the database layer using ActiveRecord).


What I like about the views in rails is that the views can be directly rendered from the actions(no clumsy xml mapping required). Also, the flexibility of using partials in my rhtml code.

A little bit about :partials - partials are something similar to 'include' tags in html where one can create parts of a webpage and bring them together using 'include file'. Creating multiple html files helps reuse of contents on a website - so that one can show a consistent header menu for example all through the website - and also keeps the html code DRY.

:partial builds on this idea. In rails one can create an rhtml file(which is actually interpretable ruby) that rails automagically handles for you to create html content. What can further be done is separate code that one wants to reuse in a file that starts with an underscore eg. _photos.rhtml.

There are multiple benefits of this breaking of code



  • code is now more readable - when u see a partial that you are not concerned about u can just ignore it and focus on the task u are doing(say u have _photos and _videos partials and you are currently working on the videos part - you dont have to worry about what the _photos partial is doing)

  • code is much more unit testable - rspec is excellent at testing :partials (My colleagues Mike and Jake can help you with that)

  • code is much more testable as a whole - mocking out partials at a time can greatly simplify testing - again rspec is your friend.

  • code is DRY


In your 'my multimedia page' (multimedia.rhtml) can look like
<% @some_variables %>
<% some_other_variables %>
render :partial => 'path/to/photos/photos.rhtml'
<% some_more_ruby_code %>

when _photo.rhtml looks like
<% @some_variable1 manipulation %>Tags: , , , , ,
<% @some_variable2 manipulation %>

The beauty here is the simplicity with which the _photos.rhtml can be separated. What rails does more is that the '@some_variables' that showed up in  multimedia.rhtml is automatically available to the :partial. Isn't that awesome one would say - and it is for a start.
But when there are many of these rhtml file that render more of these partials and all the variables are now the @variables one can get lost. Not only that the application is now getting too open - Let me explain.
With a tree of these partials multimedia -> photos -> photo -> photo_detail,  if we follow the above pattern, the @variables from multimedia would now be available in photo_detail and this is when everything starts falling apart. Now when you refactor photo_detail you have to worry about how the @variables.
This becomes more painful if you want to split the photo_detail and use the split portions together and separately at the same time. Then the problem of every :partial knowing too much about every other :partial - soon becoming a hairball so big that even cats will choke on them.
To prevent this from happening one should think about :partials as objects - this also goes well with the ruby philosophy of everything being an object.
So when I say :partials = objects all the laws of encapsulation apply.
Having said that the multimedia.rhtml now looks like so
<% @some_variables %>
<% some_other_variables %>
render :partial => 'path/to/photos/photos.rhtml', :locals => {:photo_variable => @some_variable1, :photo_caption => @some_variable2, :photo_credit => some_other_variable1 }
<% some_more_ruby_code %>

Notice that now the :partial can only care about the local variables that are passed to them it can be simplified to look like
<% photo_variable manipulation %>
<% photo_caption manipulation %>
<% photo_credit manipulation %>

Notice how all the @ signs disappeared in the :partial. Also note how I could pass in a local variable from the rhtml to the :partial.


This resolves a few issues :
- Unit testing becomes a breeze - Since the :partial is encapsulated testing the :partial in isolation is easy.
- Reusing the :partial becomes easy  - No more grand_parent to grand_son global variables
- refactoring within the :partial - not a problem - because you can write tests for this new functionality and refactor to create new partials - total insulation from the rest of the code.
- refactoring the caller of the :partial - even better since now you do not have to be afraid of breaking something because you removed an @variable.


This technique did help us refactor and increase our testing levels drastically.
Hope it helps you too.


April 10, 2007

Firefox, MySQL, Rails quirks

Posted by Sudhindra Rao

Firefox


Firefox 2.0 has a new feature. It can remember sessions and reopen/reconnect to the same urls if Firefox was closed abruptly.


This helps a lot when you have to restart your Windows machine every so often. This feature becomes annoying especially when you want to automatically open Firefox, run Selenium tests and then close it.


Since this closing of the browser is considered abrupt(even though the tests are done the browser process is killed when we automate with Selenium) and the next time you open firefox - a popup appears asking you whether you want to 'Restore Session'. For the automation programs it is almost impossible to detect this pop-up and interact with it.


Firefox allows you to disable this feature and much more.


The trick is to type 'about:config' in your Firefox address box. It shows you all the parameters you can set(including the ones for your plugins/addons).


There is a cool filter which lets you narrow down your search for a parameter and change it. The one I am referring to for 'Restore Session' is 'browser.sessionstore.enabled'


Rails 1.2 and '.' separators


Starting Rails1.2 routes.rb makes as reserved and you cannot use a '.' in your routes.


Enter :requirements - The workaround to using '.' in your route elements.


For example:


map.connect '/:controller/:action/:id', :controller => 'wiki_pages', 
:action => 'show',
:requirements => {:id => /.*/}

This allows :id to contain '.'s. A nice thing to note is that it supports any regexp.(ref : http://forum.textdrive.com/viewtopic.php?pid=116742)


MySQL and Windows


MySQL goes away(ref : http://dev.mysql.com/doc/refman/5.0/en/gone-away.html) literally a number of times during the day on Windows. The solutions mentioned in this document


seem reasonable and logical but do not necessarily work. What worked this time is a machine restart. I also tried tweaking and setting all timeout values to a very large number but the problem does not completely go away.


Roderick wrote this MySQL-Ruby-Windows thing. Worth a try if it can fix the windows timeout issue.




 Tags: , , , , , , selenium



After struggliing with running Selenium with Cruisecontrol I hit on a new solution today and am now able to run Selenium and post results more consistently than before. This way of doing it has the advantages that I did not need to run a rails app to collect results. I have managed to do it with Webrick. Since it was my first time working with Webrick it took me lot longer than I thought.


To start with, here is the problem I was facing. I was trying to do the Cruisecontrol with Selenium all over again on a fresh setup. Once I installed Cruisecontrol and Java(u have to have Java - I had ruby already). I was ready to run the same batch file I had. But I was not able to post the results. Installing a rails application with all the required gems seemed painful and an overkill for the purpose. So I started poking around with WEBrick. Some documentation on WEBrick website got me started and I could send a request to the server. But what I was not able to do was read the request parameters. All the documentation I looked up talked about the same kinds of examples as on the WEBrick website. They all mentioned and showed with an example how to handle simple requests and responses. Printing out the request did not help as it did not have parameters anywhere. On further digging I found some code in the PickAxe book (edition 2 page 248)


where the example spelled out exactly where the query object was 'req.query'. That was where one will find the parameters hash that Selenium populates when it posts results.


Here is a reference of the code that achieved the desired result.



require 'webrick'

if ARGV.empty?
p "Usage: ruby post_results.rb browser_name"
p "Example: ruby post_results.rb iexplore"
exit(0)
end

@browser = ARGV[0]
url = APPLICATION_UNDER_TEST #"http://www.yourapp.com"
testSuite=PATH_OF_TESTSUITE_RELATIVE_TO_SELENIUM
resultsURL= "http://localhost:2000/postResults" #the server that gathers the results..
testRunner="#{url}/selenium/core/TestRunner.html?test=#{testSuite}&auto=true&resultsUrl=#{resultsURL}"
command = "#{@browser} \"#{testRunner}\""

system "start #{command}" #DOS batch command start starts the command in a new thread

include WEBrick

s = HTTPServer.new( :Port => 2000 )

# HTTPServer#mount_proc(path){req, res ...}
# You can mount also a block by `mount_proc'.
# This block is called when GET or POST.
def save_to_file(params)
Dir.mkdir("results") unless FileTest.exist?("results")
Dir.chdir("results")
Dir.mkdir(@browser) unless FileTest.exist?(@browser)
Dir.chdir(@browser)
results_file_name = "selenium-results.html"

save to file as before

end

s.mount_proc("/postResults"){|req, res|
params = req.query
results_file_name = save_to_file(params)
s.shutdown
system "TASKKILL /F /IM #{@browser}.exe" #KILL the browser since selenium is done.
}

trap("INT"){ s.shutdown }
s.start

March 2, 2007

Automated Testing and Continuous Integration with Selenium

Posted by Sudhindra Rao

For those who are closely following - this post has been edited for clarity of content and language ;) - ohh and to fit your screen

Selenium is a nice and slim tool for testing. If I may start with the history it was built by Jason Huggins and Paul Gross* from ThoughtWorks and then opensourced on http://www.openqa.com/selenium (List of all the contributors can be found there too).There are 2 versions of Selenium - Selenium Remote Control and Selenium core. Selenium core is the essence of Selenium where as Selenium RC packages the core to enable you to write tests in the language of your choice. There is a recorder (and much more) with Firefox which allows you to point and click and create your tests in any of the languages that Selenium RC supports.


All these niceties made Selenium the choice of testing tool at my current project.

Since it was designed by developers we also thought that Selenium was easy to automate. But as it turned out not so much. Here is the issues we faced and what we did to fix it.

We tried to run Selenium in IE just to realise that we have a same origin policy problem - we had our tests open http://localhost/ and then navigate to http://location.localhost/. Yes you guessed it our domain name changed during the test run. This is what Selenium cannot handle. As per the Same Origin Policy your javascript(Selenium tests/testrunner) cannot run/access content that is on a different domain, protocol or port. If you still would like to use Selenium you have to use the experimental modes (chrome for Firefox or hta for IE) which are really beta and flaky.


So having thrown that out of consideration we had to run tests from the same machine as the application under test(AUT).


We decided to go with Selenium core.(since Selenium RC for some reason stripped our pages of css - ugh?)

To do this we installed Selenium core in our application code ie in the public directory(since ours is a ruby app - for a regular java app in apache tomcat for example webapps would be the equivalent location). Then we could run TestRunner with our TestSuite and fire the tests.


The next step was to automate all this testing so that it runs periodically and runs as part of a build so that your application is functionally tested continuously.

Some details you would like to note before we dig deeper.

Selenium core just runs as a url :

Our URL was as follows

http://localhost:3000/selenium/core/TestRunner.html?test=../tests/TestSuite.html&auto=true&resultsUrl=http://localhost:3000/postResults?browser=ie6


This URL does a few things -

  1. it specifies a test suite TestSuite.html
  2. that the tests are going to run automatically (basically as soon as you point your browser to the above URL that is)
  3. they are going to post results to a results URL.

All this is well documented. What is not documented clearly is that one needs to write a POST handler (our POST handler is at http://localhost:3000/postResults). This could be the hard one if a non-developer is trying to set this thing up.


I have written a quick and dirty one in ruby which I will post here for reference

class PostResultsController < ApplicationController
def index
browser_name = params["browser"]
time = Time.now
timestamp = "#{time.month}/#{time.day}/#{time.year} #{time.hour}:#{time.min}:#{time.sec}"
results_file_name = "public/results/#{browser_name}(#{timestamp})-results.html"

f = File.new(results_file_name, "w+")
# f.write(params)

f.write("<html><LINK href='selenium-test.css' type=text/css rel=stylesheet><body>")
result = params["result"]
f.write("<table>")

f.write("<tr>")
f.write("<td>Result : #{result}</td>")
numTestTotal = params["numTestTotal"]
f.write("<td>Tests Total : #{numTestTotal}</td>")
f.write("</tr>")

f.write("<tr>")
totalTime = params["totalTime"]
f.write("<td>Total Time : #{totalTime}secs.</td>")
f.write("</tr>")

f.write("<tr>")
num_test_passes = params["numTestPasses"]
f.write("<td>Tests Passed : #{num_test_passes}</td>")
num_command_passes = params["numCommandPasses"]
f.write("<td>Commands Passed : #{num_command_passes}</td>")
f.write("</tr>")

f.write("<tr>")
numTestFailures = params["numTestFailures"]
f.write("<td>Tests Failed : #{numTestFailures}</td>")
numCommandFailures = params["numCommandFailures"]
f.write("<td>Commands Failed : #{numCommandFailures}</td>")
numCommandErrors = params["numCommandErrors"]
f.write("<td>Commands Errored : #{numCommandErrors}</td>")
f.write("</tr>")
f.write("</table>")
totalTests = params["numTestTotal"].to_i - 1
for i in 1..totalTests
index_str = 'testTable.' + i.to_s
testTable = params[index_str]
f.write(testTable)
end
f.write("</body></html>")
f.close
end

end



Another thing that I did not allude to was the browser=ie6 parameter on the results URL. This was another requirement for our scripts -that they be run in 6 different browsers and post results for us to look at. That means I have to know for each test what I am running against and this extra parameter on the resultsURL would be nice to have.


To make sure Selenium understood it I had to change selenium-test-runner.js as follows


add the following to HtmlTestRunnerControlPanel prototype

getBrowser: function() {
return this._getQueryParameter("browser");
},


and then use it in TestResult prototype like so


//added getBrowser so that we can run tests on a browser and send the browser name with results.
var browser = this.controlPanel.getBrowser();


If this is not enough Selenium also adds a constraint on how you automate using cruisecontrol. The only way to do that is to fire the browser for test with the URL like above. So cruisecontrol has to take the help of a batch file to automate this - which is less than ideal.

Cruisecontrol by design waits for the build tasks to complete(in this case the batch file). But as the tests run inside the browser Cruise has no way to know after they have finished as browser does not change state.

We had to do something like this in our batch file


set PATH = %PATH%;path_to_ie

start iexplore.exe selenium_url_like_above

rem approx wait of 60secs hack

ping -n 61 127.0.0.1 > nul

taskkill /F /IM iexplore.exe


This batch file above will set the path so that IE can be run. then 'start' the browser in a separate process then wait for 60 secs and then kill IE. Once IE is killed the batch process is over and cruise can wakeup.

You can automate running Selenium tests for any browser as shown above. Just make sure your ping interval is set to appropriate time so that the browser is closed only after the tests are done not before.

(A good reference for batch files and command prompt automation is - http://www.allenware.com/icsw/icswidx.htm. I found the info for 'start' and the hack for sleep using ping here)

2 more hacks are on my mind but I will post in a later discussion.

Phew! This has been my first blog post in a long time. Thanks for the patience.


Welcome