Showing posts with label ROR. Show all posts
Showing posts with label ROR. Show all posts

Wednesday, November 18, 2009

Load default lookup data with Rails fixture



The Rails Fixture is a great feature to load sample test data before running unit, functional or integration tests. While it is mostly used in loading test data, it can be a handy tool to load default lookup data for the staging or production database. We can use the “create_fixtures” class method of “Fixture” class.

The following code snippet loads the data from the “countries.yml” file into the “countries” table.

require 'active_record/fixtures'

class CountryData
 def load()
   fixtures_folder = File.join(RAILS_ROOT, 'test', 'fixtures')
   fixtures.create_fixtures(fixtures_folder , " countries " )
 end
end


In this case,the “countries.yml” file must be present in the “<RAILS_ROOT>\test\fixtures” folder.

If we need to load data from all the “.yml” files of a particular folder and don’t want to hard code the names of the individual files as the above code snippet, the following code will do.

require 'active_record/fixtures'

class LoadDefaultData
 def load()
   fixt_folder = File.join(RAILS_ROOT, 'test', 'fixtures')
   fixtures = Dir[File.join( fixt_folder, '*.yml')].map {|f|
   File.basename(f, '.yml') }
   Fixtures.create_fixtures(fixt_folder, fixtures)
 end
end

Sunday, July 26, 2009

Mocks and Stubs in ROR with Mocha



Mock objects and stubs are very useful strategies in Test Driven Development(TDD).They play a great role in increasing the effectiveness and speed of the unit tests.

Life is easier when we write unit tests for methods that returns in-process immediate results.Like adding two integers and returning the result.Things get complicated when the method interacts with applications in another process and involve in a blocking call (connecting to database,calling external web service methods,sending mail through a mail server).

For a variety of reasons, interacting with external resources can make out automated tests a lot harder to write, debug, and understand.It also takes a significant amount of time to run the tests.To extend unit test coverage and overcome this limitation,an alternative best practice is to use mock objects, stubs, or other fake objects in place of the external resources that initiate an inter process communication.

The terms "Mock" and "Stub" are used interchangeably in most cases and thought as same.But there are subtle difference in the usage of these two.Martin Fowler explains the difference in this renowned article.

In my understanding so far,Mocks and Stubs are similar in nature but Mocks does more in the form of "Interaction based testing".

Stub is a class where the method definitions imitates original methods of a class that is involved in calling external resources.The imitated methods returns hard coded known result.So the stub methods return expected outputs against a set of known inputs without involving call to external resources as the original class methods do.

For example,if the original "execute_DML" method involves initiating a connection to database,executing the SQL query against it and returning a true/false, the stub "execute_DML" method just returns true or false depending on the input.

Mocks does the same as Stubs, but it considers the interaction between classes.We use mock objects to record and verify the interaction.In the above example the mock "execute_DML" method not only returns true/false but also verifies that the method calls the appropriate methods to initiate the connection and sending the SQL query to database.In this case, the connection and query execution methods are also mocked to avoid external resource calls.

In mock methods, if we set an expectation to call a method of a particular object only once and in reality the method is called twice, the test result is considered a failure even if the final outcome of the method is satisfied.This is different from stubs.

In ScrumPad project, we use mocks and stubs in the above mentioned scenarios.We use an excellent framework Mocha for this.

Mocha is a library for mocking and stubbing that provides a unified, simple and readable syntax.Let me provide some examples:


We can mock a "class" method.suppose we have a method "add_to_cart".The original definition is the following,

def add_to_cart(product_id)
 product = Product.find(product_id)

 if (product != nil)
   Cart.add(product)
 end

 return true
end


The call to "Product.find" involves a database interaction.We can use mock methods to avoid this.


require 'test/unit'
require 'mocha'

class MiscExampleTest < Test::Unit::TestCase

 def test_mocking_a_class_method
  product = Product.new
  Product.expects(:find).with(1).returns(product)
  assert_equal add_to_cart(1), true
 end
end


In the above case,we have set an expectation in the "add_to_cart" method on "Product" class.Inside the "add_to_cart" method, there must be a call to the "find" method of "Product" class.If the call is omitted,the test will fail even if the final assertion is passed (e.g. the return value of the method "add_to_cart" is true).As we have mocked the "find" class method of the "Product" active record class,there will be no actual database interaction.

We can also set how many times a particular method should be invoked.For example if we set the expectation in the above example like this:

Product.expects(:find).once.with(1).returns(product)

This will raise error if we call the "find" method more than once inside the "add_to_cart" method.

We can set expectation on "instance" methods in stead of "class" methods.For example

Product.any_instance.expects(:find).with(1).returns(product)


Using Stubs instead of mocks is very similar in mocha.For example:


def test_stubbing_an_instance_method_on_all_instances_of_a_class
 recepient_token = "124399A_@@44"
 PaymentService.any_instance.stubs(:create_recipient_token).
 returns(recepient_token)

 transaction_response = TransactionResponse.new()
 transaction_response.stubs(:status).
 returns(SUCCESS)

 PaymentEngine.handle_payments()
end


In the above example, we are using stubs to return predefined results from the imitated methods.

Inside the "handle_payments" method,calls to "create_recipient_token" method of "PaymentService" web service will not invoke the web service in reality but will just return the hard-coded string.

Similarly calls to the "status" method of "TransactionResponse" class will always return "Success".

In both cases we are not setting any interaction based expectations as we did with mocks (the "expects" keyword in mocha).We are just freezing the external resource invocation by returning the known result with "Stubs".

The simplicity of "mocha" and usage of these two extremely powerful tool helping us a lot in increasing the test code coverage of ScrumPad.

Monday, May 4, 2009

Creating a Private Ruby Gem Server



While installing a new ruby gem in a machine,we normally use the gem install <gem name> command.This command downloads the latest version of a particular gem.The problem is that ROR gems are not always backward compatible.So if a ROR application was developed using a particular version of a gem (Ex:0.1.1) and the latest version of the gem in the internet (normally rubyforge.org) is 0.1.2,problem may arise if the version 0.1.2 is not backward compatible with version 0.1.1.This may cause a serious trouble when the application is deployed into the web server and the wrong version of the gem is installed.Most probably the application will not function as expected.

A solution to this problem is to deploy the exact version of all the gems those were used during development.One way to implement this is hosting the correct version of the gems into a web server(thus call it a private gem server).I have tested this option in a Apache web server.The steps are as follows:

1.Create a directory for hosting gems on the public files area of the web server.Let's refer is as <Base Directory>.

ssh user@web.server
cd /var/www
mkdir my_gem_server


2.Create a sub directory called "gems" under the <Base Directory>.The name of the sub directory must be "gems" by convention.

ssh user@web.server
cd /var/www/my_gem_server
mkdir gems


3.Copy all the necessary gems from the development machine into the /var/www/my_gem_server/gems sub directory.

4.Generate the gem index:

gem comes with a command generate_index which generates all of the files necessary for serving gems over HTTP.Run this command into the <Base Directory> (/var/www/my_gem_server).

gem generate_index -d /var/www/my_gem_server

Now the private gem server is ready to serve the gems for download and install.

To install the private gems into the target web server where the ROR application will be deployed,log in and run the following command.

gem install <gem name> --source http://<private gem server>/<Base Directory>

The correct version of the gems will be installed and thus the application integrity will be maintained.We need to rerun the gem generate_index command each time we add or remove a gem from the private gem server.