Thursday 25 March 2010

Rails - Day 11 - Model Testing and ActiveRecord 101

Rails  - Day 1 - The RubyGem Love Story
Rails -  Day 10 - Testing Rails Controllers
Rails -  Day 13 -ActiveRecord Relationships Part II
Rails - Day 15 - Rails Forms Part II
In the previous post, we touched on rails controller testing, and now in this post, I want to touch on Model testing.

In Rails, unit testing is what you do when you test your models.  Model testing is where we should write the majority of our tests.  

The mantra Fat Models and skinny Controllers is one often associated with the MVC pattern and a controller action that does too much obscures the intention of the action.  A controller action should be intention revealing.  I start to get suspicious of my controller actions if they start stretching over five lines of code.  Often my actions are just one line of code like below:



As I mentioned in the last post, we have created a fictitious application to record the expenses that are incurred when running a business.  Examples of such expenses are Travel Expenses or Office equipment.

It is a fair assumption to make that we will need an Expense resource abstraction to model this real world entity and we have previously generated the rails ruby files that we will need with the ruby script/generate resource expense command which has generated the following files:



If this was a real application that I was working on for a real client then I would create the UI first and then think about the model.  I find that driving the UI out first really helps with defining the structure of my model.  It also gives me a shorter feedback loop with the client.  

I have done the same for this silly application and below you can the page that we will use to both create and update an individual expense item:



I can assume from the UI above, that the Expense model object is a complex object, meaning the expense object will have child objects that define its structure.

I am going to leave the discussion about defining relationships between objects in ActiveRecord until the next post.

For this post, we are going to create the Expense object itself using TDD.  As previously stated, we have ran the generate script to add an expense resource that has created and stubbed out a number of files for us.

ActiveRecord, Migrations and Models

ActiveRecord is the ORM (Object Relational Mapper) layer used in rails.  It is used by controllers as a proxy to the database tables.  The obvious advantage of an ORM is that it saves you from having to write tedious SQL.  

The basic concept in ActiveRecord is the model.  Each model class is stored in the app/models directory inside your application, in it's own file.  In our application, we have a model file in app/models/expense.rb that was created by the script generator:


Each model will generally correspond to a table in the database.  The name of the table is by convention, the pluaralised, lower-case form of the model's class name.  So in our case, we have an Expense model and an expenses database table.

I mentioned migrations in a previous post and the ruby script/generate resource Expense command has created for us a create migration for the expense resource:


You can see from the above that only the default fields have been created.  If we run rake db:migrate then the expenses table will be created. We have already done this in order to create just enough server code to get our UI working.  Migrations are part of ActiveRecord and enable a rails developer to generate a database structure using a series of Ruby script files.

Using the UI screenshot as a guide, we are going to write a test to ensure we create the correct fields for the expense model.

As we are going to be adding fields onto our model, we are going to create a unit test.  The script generator has kindly created for us a unit test file for this very purpose in /test/unit/expense_test.rb.


So far this test file only contains one default test case which we will remove.

Using the UI as our guide, we want to test that we can record the relevant information of the Expense model object.

We want to record the following:

  • Whether the expense was paid by a director. we will create a paid_by_director field which will be of type boolean.
  • The date the expense was paid.  We will create a expense_date field of type date.
  • An external reference identifier we can use to tie our receipt to the expense.  We will create an external_reference field of type string.
  • A long description of the what the expense was.  We will create a description field of type string.

We will  need other objects to record the other data in the screen shot to store the entire expense information.  We will cover relationships between objects in the next post.  For now, we will just work on the simple expense model.

As we are good TDD soldiers and do not want our ALT.NET friends scoffing at us at the next open spaces forum, we want to write our model test code first.  What this means is, we will write the test code before the application code.  In all seriousness, TDD has saved me many times from disappearing down very dark rabbit holes.  With TDD, you first write a failing test and then write enough code to make the test pass.  This seriously keeps you focused on the job in hand.  I have also found this a great way of inspiring myself to provide a solution when faced with writers' block.

Using the UI as my guide, I write the following test:



As I mentioned in the previous post, we are using rake to run our tests and we can use rake to run only the unit test of our project with the command rake test:units.

As expected, the test fails with the following confirmation:


We can see here that it is telling us we have an undefined method paid_by_director which is absolutely true.

We will now add the fields to the object. 

Table first or Model First?

In .NET and with Nhibernate, the way I like to work is to first drive out the model using TDD, I then create the mapping files and lastly, I run SchemaUpdate to generate the database structure.  In rails it is slightly different in that you define the database tables first.  The model then picks up these fields that are added to the table by convention.  To be perfectly honest, the Nhibernate way feels like the more pure and righteous path but the productivity gains of rails make me feel less dirty about the whole thing.  The coupling to the database as a result of the ActiveRecord pattern is something I have learnt to feel less bad about.  In my heart of hearts I know such a coupling is bad but the productivity hit is good.  The scales of justice swing to the right for me on this one.  

In order to add these new fields to our expense database table that will enable our test to pass, we are going to have to create a new migration to add the columns.  We do this with the ruby script/migrate add_initial_fields_to_expenses command which creates a file in db/migrate/20100321234831_add_initial_fieilds_to_expenses.rb.

In this newly created migration file, we add the following text:



We now run the migration with the rake db:migrate command.

We are now ready to re-run our tests with the rake test:units command. It should be no surprise that the unit test and assertions now pass:


Avoid Meaningless Tests

Below is an example of what on the surface seems a reasonable thing to test but in reality is a bit of a waste of time.



The above is a bit of a silly test because you are not testing your own code.  You are in affect testing the ActiveRecord library which I can say with some degree of confidence is pretty damn well tested.

We should be testing model logic, not the ActiveRecord framework.  You should be testing code that you write.  Anything that gets added to your model needs test coverage.

Mocking

I lastly want to touch on mocking in Rails.  Before I discovered mocking, my tests were always written against resources such as the database or a webservice call.  Such tests are brittle in the extreme.

In the last post, I covered writing functional tests that we use for covering controller actions.  I also stated that, the index action of a controller is generally used to display a list or subset of the requested resource.  This would 999 times out of 1000 require a call to the database.  The index action of our ExpensesController would probably look like the following:


As I stated earlier, testing the all method of the ActiveRecord framework is pretty senseless as we want to test code that you write and not framework code.  But with this functional test, we are testing that we get the correct HTTP repsonse and that the correct template file is selected.

As these are functional tests, I am unsure what the correct party line is here, would you mock these calls or would make the calls directly to the test environment database? If anybody is reading this and has an opinion on this, they can please leave a comment. I am personally not a huge believer in integration tests and the maintenance involved, so I generally write very few of these. We can always use an in memory database for our test environment like SQLLite but I still prefer to mock the majority of these calls.
Anyway, for the sake of this example, I am going to mock this call to the database. I am going to use FlexMock as my mocking library of choice which is easily installed via the beautiful RubyGems with the command gem install flexmock.
Next, I update the previously mentioned test to the following:


The line of interest is the following:


Here we are telling the flexmock framework that we are expecting a call to all from the Expense class instance and when this happens an empty array should be returned.  It really is as easy as that.

I will leave things there for now.  There are a million tutorials about testing in Rails and you should dig deeper if intrigued.

In the next post, I am going to move onto defining relationships in ActiveRecord.











No comments:

Post a Comment