Testing workflow

From diaspora* project wiki
Revision as of 22:23, 26 September 2012 by Sean Tilley (talk | contribs) (work-in-progress)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

In order to assure code quality, ease refactoring and to make sure only the code that is really necessary gets written we strive to encourage TDD ([Test Driven Development][tdd]) or BDD ([Behaviour Driven Development][bdd]) when possible. There is a huge list of benefits that result from this style of development and even some research on it referenced from the Wikipedia articles, if you are interested in reading up on that topic.

<img align="right" src="Test-driven_development.PNG" width="440" /> In essence, TDD means that before you write some actual code, you are supposed to write a testcase firsts, using the feature or function the way it should represent itself to the outside. Of course, because no actual code has been written yet, the testcase will fail if being run. Now the idea is to write only the minimal amount of code that gets the testcase to pass and as soon as it does so, refactor the code until you are left with perfectly structured and modular code, that even passes your super-awesome testsuite.

Now, this can be done on several layers, starting at single functions, methods and classes up to what the user gets presented with in the UI. There are different tools for each one of these.


Since Diaspora is based on [Ruby on Rails][ror], we get the rspec testing environment practically for free. Our [rspec tests][rspec] are located in the spec/ directory and that directory is split up in subdirectories differentiating which parts of the code are being tested with the containing files (e.g. models in spec/models/, controllers in spec/controllers/ or the Diaspora lib in spec/lib/).

Lets look at an example: If we were to test the validation and cleanup rules of the Profile model, the place to look for them is spec/models/profile_spec.rb. Inside that file you will see that the tests are written in plain ruby code structured by a DSL ([Domain Specific Language][dsl]), in this case a few helpful ruby methods that let us nest the tests logically and check the results more easily.

describe Profile do
  describe 'validation' do
    describe &quot;of last_name&quot; do
      it &quot;strips leading and trailing whitespace&quot; do
        profile = Factory.build(:profile, :last_name =&gt; &quot; Ohba &quot;)
        profile.should be_valid
        profile.last_name.should == &quot;Ohba&quot;

This shows us that the description of the tests is supposed to be easily human-readable as this is also the part that gets shown, when the test fails ("validation" "of last_name" "strips leading and trailing whitespace"). Another thing that should catch your attention in this example is the use of a [factory][factory_girl] for creating mock objects to test with. This greatly reduces the amount of work that goes into creating somewhat realistic tests.

If you have too much time or you want to make sure you didn’t break anything apart from the stuff you were working on, you can run all rspec tasks

$ bundle exec rake spec

To run just our sigle profile spec file from the example

$ bundle exec rspec spec/models/profile_spec.rb

If your test is failing, the command output will present you this

=> Building fixtures
=> Built aspect_memberships.yml, aspects.yml, contacts.yml, people.yml, profiles.yml, and users.yml
Run options: exclude {:performance=>true}

  1) Profile validation of first_name fails on purpose
     Failure/Error: false.should == true
     expected: true
     got: false (using ==)
     # ./spec/models/profile_spec.rb:11:in `block (4 levels) in <top (required)>'

  47/47:       100% |==========================================================================| Time: 00:00:00

Finished in 0.95665 seconds
47 examples, 1 failure

Failed examples:

rspec ./spec/models/profile_spec.rb:10 # Profile validation of first_name fails on purpose

Randomized with seed 28937

If all of your tests are fine, it will show you a summary and some statistics similar to the following

``` => Building fixtures => Built aspect_memberships.yml, aspects.yml, contacts.yml, people.yml, profiles.yml, and users.yml Run options: exclude {:performance=>true} 46/46: 100% |==========================================================================| Time: 00:00:00