Feed on
Posts
Comments

Unit Testing on the IPhone

Writing unit tests for an IPhone application is unfortunately a bit more involved than it need be. Here’s how you can get it going for your project (it’s really worth your while doing this at the start of the project, and writing tests as you implement new features but you already know that, right?).

Google have a project called Google Toolbox for Mac located at http://code.google.com/p/google-toolbox-for-mac/ - download the latest version and unpack it directly into your project. Rename the directory to “google-toolbox-for-mac” (it will make it easier to follow along).

Open your project in XCode and then choose Project > New Target…
Pick Cocoa Touch Application as the template and name the target “Unit Tests”. Choose Project > Set Active Target > Unit Tests.

Choose Project > Add to Project… and pick the following files (make sure each are added to the “Unit Tests” target:

google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestMain.m
google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestDelegate.m
google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestDelegate.h
google-toolbox-for-mac/UnitTesting/GTMSenTestCase.m
google-toolbox-for-mac/UnitTesting/GTMSenTestCase.h
google-toolbox-for-mac/GTMDefines.h

Right-click on Unit Tests in the Targets group and choose Add > New Build Phase > New Run Script Build Phase

Paste this text into the Script box:

${INPUT_FILE_DIR}/google-toolbox-for-mac/UnitTesting/RunIPhoneUnitTest.sh

Try a build now (Cmd-B) and look in the build log. You should see a line like this:

Executed 0 tests, with 0 failures (0 unexpected) in 0.002 (0.002) seconds

Switch back to your normal target and verify that it still builds OK. This is probably a good time to commit everything into source control.

Adding a unit test

Choose File > New File… and select “Objective-C test case class” from the Mac OS X Cocoa group. Call it FooTest.m if you’re testing a Foo class. Edit the FooTest.h file and replace the existing #import line with:

#import "GTMSenTestCase.h"

That’s it! Add individual test methods and run the tests by doing a build (Cmd-B)

Many web applications have to run periodic tasks and it’s really important to decouple these tasks from the clients request thread. One well-proven way to manage these is to use cron to execute the tasks. I had to do this recently to send reminder emails to authors in StatusHub and thought I’d share how easy it was.

First, create a rake task to provide an easy interface to the tasks. This makes it easy to test them out or manually run them. I created a file in lib/tasks called cron.rake:

namespace :cron do
  desc "Send email reminders to all authors on upcoming and overdue reports"
  task :send_reminders => :environment do
    Report.send_reminders
  end
end

Here, Report is one of my models and send_reminders is a class method on that:

def self.send_reminders
  Report.due_drafts.find_each do |report|
    Mailer.deliver_report_due report
  end
end

The Mailer model is a subclass of ActionMailer::Base and has methods for each type of message you want to send. This one looks like:

def report_due(report)
  recipients report.author.email
  from       "StatusHub<support@statushub.com&rt;"
  subject    "Your report entitled '#{report.title}' is #{report.overdue? ? 'overdue' : 'due'}"
  sent_on    Time.now
  content_type "text/plain"
  body       :report => report, :url => design_report_url(report, :host => report.author.account.host)
end

Calling Mailer.deliver_report_due invokes the report_due method and then renders report_due.html.erb into the body of the email. You can now test the task with:

$ rake cron:send_reminders

On the production server, the job can now be automated by adding the following like to your crontab (edit with ‘crontab -e‘):

00 04 * * * cd /app/statushub/current && /usr/bin/rake RAILS_ENV=production cron:send_reminders

I was at the Future of Web Apps Dublin conference on Friday and a one of the speakers (I think it was Des from contrast.ie) showed a screen from a wireframing tool called Balsamiq. Here are my thoughts after using it for a little while…

At its heart, Balsamiq is a vector drawing tool with a library of built in ’smart’ shapes like OmniGraffle or Visio. The key difference is that instead of having realistic-looking controls to place on the page, the controls are drawn in a cartoony style. A typical page looks like this:

mytube.gif
The fact that the design is not pixel-perfect is actually a huge time saver. I’ve often started in OmniGraffle with the intent of just sketching out how the page should be laid out or how the user should interact with it, and then ended up many hours later tweaking colours and fonts and alignment because I can’t ignore the fact that they’re ‘wrong’. Well, in Balsamiq, the effect is much more like using a whiteboard - when’s the last time you rubbed out what you just wrote on a whiteboard because the font was wrong??

The timesaving in the design of a page also carries over to group review of the page. The fact that it’s ‘just a wireframe’ forces people to evaluate the design at that level.

In terms of negatives, the only gripe I have is that the interface is written using Adobe AIR which means that it definitely has a non-native feel to it. Nothing fatal, just a bit jarring in places.

First, get an SSL Cert. In a quick search, GoDaddy seem to be one of the cheaper options at $29.99/year. Pick the ‘Standard SSL’ option.

Important: When prompted for a passphrase, just press enter. Otherwise you’ll have to enter it every time you start apache.

$ openssl genrsa -des3 -out clikboard.key 1024
$ openssl req -new -key clikboard.key -out clikboard.csr

On the GoDaddy site, click on the certificate you’ve just bought and you will be taken to a screen where you confirm the contact details and a place to host the certificate signing request. Paste the contents of clikboard.csr into this area. Click next and shortly afterwards you’ll receive an email with a link to the certificate zipfile (containing, in my case, 2 files - gd_bundle.crt and clikboard.com.crt). Copy these onto your production server.

On the server, edit /etc/apache2/sites-available/clikboard (replace this with your site config) to make a copy of the VirtualHost section and replace the :80 with :443 and add the following lines:

SSLEngine on
SSLCertificateFile /home/denis/ssl/clikboard.com.crt
SSLCertificateKeyFile /home/denis/ssl/clikboard.key
SSLCertificateChainFile /home/denis/ssl/gd_bundle.crt

Then, enable SSL and restart apache:

$ sudo a2enmod ssl
$ sudo /etc/init.d/apache2 force-reload

Verify that the site now works with both http and https urls. Of course, all this is apache stuff and really nothing to do with rails. The last part of getting it working with your rails app is actually trivial:

./plugin/install ssl_requirement

Then, follow the instructions in vendor/plugins/ssl_requirement/README.

As we were approaching the launch date for clikboard.com (at least once we were adding items to the Lighthouse list slower than we were implementing them), we realised that we needed a cool logo that people would remember and associate with the site. After briefly flirting with the idea drawing it ourselves, we decided to go with an outside designer.

I had heard of design contests before and thought this would be an interested excuse to try them out. Here’s what happened…

First, I picked 99designs to run the contest. They’re pretty new but have a clean, transparent service and have a good number of designers available. The contests are fixed to run for exactly one week which seemed perfect. The cost for running a contest is $39 (assuming you don’t add any extras). You have to set a prize amount which is what you pay for the winning design. I set it to $300 which seemed typical.

Second, I wrote a design brief. This is pretty important because you have to give a design something to start from. The way it seems to work is that they come up with concepts from the brief, and then evolve the designs based on your feedback.

Once the contest was launched, we had the first designs within hours! We realised that it was really important to send meaningful feedback as quickly as possible so that the designers could act on it. Because the designers were from all over the world, new designs were coming in right around the clock!

By the end of the week, we had 45 designs. You can see them at our contest page - In the end we went with this design (it captured the ‘click’ and ‘board’ part best and has a distinctive favicon):

medium_logo.png

Overall, the experience was very positive. In the past I’ve worked with designers where you just don’t ‘click’ (no pun) and it’s hard to get him to understand what you’re looking for. By working with a group and scoring the designs as you go, it seems easier to steer the design and get what you want.

Since Rails 2.0.2, Sqlite has been the default database for new projects. This is great to get you started but at some stage you’re likely to want to move to a bigger DB like mysql or postgres. Here’s the steps involved:

Edit your config/database.yml file and make it look like this (change the database names, username and passwords to match your environment):

development:
  adapter: mysql
  encoding: utf8
  database: myapp_development
  username: root
  password:
  socket: /tmp/mysql.sock

test:
  adapter: mysql
  encoding: utf8
  database: myapp_test
  username: root
  password:
  socket: /tmp/mysql.sock

production:
  adapter: mysql
  encoding: utf8
  database: myapp_production
  username: root
  password:
  socket: /tmp/mysql.sock

Then, recreate your databases and tables:

$ rake db:create:all
$ rake db:migrate

That’s it!

One of the things that easily catches people out with rcov is that it doesn’t show you the coverage of all your code, it just shows the coverage of the code touched by your tests. So, if you completely forget to add tests for a new controller, that won’t appear in your coverage report.

Here’s an easy fix: save the following code as spec/app_spec.rb:

require File.dirname(__FILE__) + '/spec_helper'

dir = File.dirname(__FILE__)
Dir[File.expand_path("#{dir}/../app/**/*.rb")].uniq.each do |file|
  require file
end

It simply includes all the ruby files under your /app directory so that they’re reported properly by rcov.

Background - grouped links

Normally, when people talk about paging in a web application, they mean providing next/previous links and direct links to particular page numbers. Here’s some examples you should be familiar with:

digg.png

google.png

flickr.png

This style of paging is supported very well by the will_paginate plugin. However, some types of information don’t readily lend themselves to this type of paging. When listing people, for instance, it’s natural to want to jump directly to the ‘M’ names, just like you would with a telephone directory. For this type of information, what you want is a set of links that take you directly to the page with the first occurrence of a particular letter:

letters.png

The greyed letters indicate that there are no entries beginning with that letter.

I’ve needed this for a recent project and implemented it as a patch to will_paginate, it’s available at http://github.com/dhennessy/will_paginate/tree/master for anyone who feels like trying it out.

Adding grouped links to an existing project

The grouped links plugin depends on you having a grouping attribute on the data you want to display. For the example above, this might be the first letter of the name; for a recipe application, it might be the primary ingredient. The point is that this has to exist in the database as a separate column. One easy way to maintain this is to add a before_save filter to your model that computes the grouping attribute:

before_save :update_group_letter

def update_group_letter
  self.first_letter = name[0,1]
end

In your controller, you create a list of results as follows:

@people = Person.paginate :page => params[:page], :group_by => 'first_letter', :order => 'name ASC'
@people.add_missing_links(('A'..'Z').to_a)

Normally, the paginate method will add a grouped link for every distinct value of the group_by attribute. The add_missing_links method in this example will add additional links for letters that don’t already exist (which looks better in this case).

Finally, add the following to your view:

<%= will_paginate @people, :draw_if_single => true, :next_previous_links => false %>

The draw_if_single option forces the links to be displayed, even if the results all fit on a single page. Note that the resulting links will have #A, #B, etc appended to them so that you can jump to a particular row (providing you set the anchor when the first letter changes in your list). As you can see, this is very similar to the way you’re already using the will_paginate plugin.

Health Warning: this plugin should probably be treated as experimental as I may change things a bit as I get feedback from real users.

There aren’t as many quick guides to using rspec as there should be. So, to help anyone wanting a fast jumpstart, here’s how to add rspec to a new project:

$ ./script/plugin install git://github.com/dchelimsky/rspec.git
$ ./script/plugin install git://github.com/dchelimsky/rspec-rails.git
$ ./script/generate rspec

That’s all there is to it! You can run your rspec tests with this command:

$ rake spec

.. or, with code coverage output:

$ rake spec:rcov
$ open coverage/index.html

Now you’re ready to write some tests. To get started, take a look at the examples at http://rspec.info/documentation/rails/

Pay More, Save Less

I saw this today on monster.ie. Notice anything unusual about the last two rows?

monster-prices.png

Older Posts »