Skip to content

Instantly share code, notes, and snippets.

@danielberkompas
Created December 14, 2012 15:21
Show Gist options
  • Save danielberkompas/4286218 to your computer and use it in GitHub Desktop.
Save danielberkompas/4286218 to your computer and use it in GitHub Desktop.
An article about the best way to use Rspec.

Best Practices

How to Test Classes

  require "spec_helper"
  
  describe ClassName do
  
    # Describe each method on your class.
    # Note that the `describe` call limits the scope
    # of everything inside it.
    
    # This is how you describe a "class method", defined like this:
    # def self.class_method
    #   ...
    # end
    describe ".class_method" do
      # ...
    end
    
    # This is how you describe an "instance method", defined like this:
    # def instance_method
    #   ...
    # end
    describe "#instance_method" do
      # Do setup for the method with "let" syntax
      let(:some_var) { some_value }
      # etc...
      
      # Set the subject
      subject { some_var }
      
      # Run any additional setup you need
      # `before` runs before every "it" block in
      # the current scope
      before { do_some_more_setup }
      
      # When you're testing expectations like should_receive, 
      # you might find `after` useful.  It runs after every
      # "it" block in the current scope.
      after { call_some_method_that_should_trigger_expectations }
      
      # "it" and "its" implicitly refer to the subject
      it { should be_something }
      
      # Use "its" to test methods on the subject
      its(:attribute) { should be == some_value }
      
      # Does your method do different things in different
      # scenarios?  If so, use `context`.  
      context "when some criteria is the case" do
        # then actually make the criteria the case using a "let" block
        # then add your tests in that scenario
      end
      
      context "when some criteria is not the case" do
        # etc
      end
      
      # Context also limits everything inside to that context.
    end
  end

How to Test Modules

# Don't describe modules.  Use `shared_examples_for`, as shown after
# this comment.  You should store these shared examples under
# /spec/support/shared_examples/.  Make a file for each set.  In this
# case, the file should be named `module_name.rb`
shared_examples_for ModuleName do
  describe "#some_instance_method" do
    # etc
  end
end

# That way, when you include the module in a class,
# you can test it this way:
describe ClassThatIncludesModule do
  it_behaves_like ModuleName
end

Rspec Best Practices

This document is intended to help others establish better testing practices with Rspec. Feel free to comment and contribute.

1. Get Some Gems

These are some great gems that will make your life a bit easier. I'm assuming you already have Rspec set up in your project.

Guard

Guard automatically runs your tests as you make changes to your code base. (As long as you have it running, of course) This is really helpful.

  1. Add gem guard-rspec to your Gemfile under the group :test, :development
  2. Run bundle exec guard init. This sets up your project with a Guardfile.
  3. Run bundle exec guard to get guard up an running.

Fuubar

  1. Add gem 'fuubar' to your Gemfile.
  2. Alter the top of your Guardfile like this:
guard 'rspec', cli: "--format Fuubar --color", :version => 2 do

This will give you a progress bar as you run your tests, and any failures will be instantly printed out for you. That way you don't have to wait for all your tests to complete to know what failed.

2. Sign Up With Awesome Services

Using all of these services will make your life easier and ensure that the tests get written and run. Because if no one is running your tests, there is no point in writing them.

Github

The other services listed below won't be as useful for you if you aren't hosting your code on Github. The reason for this that Github has great APIs which let these services automatically attach information to your codebase.

Continuous Testing

You should put a barrier between you and your deploys by using a continuous deployment service. These services watch your branches, and automatically run your tests against each branch. When a branch is passing, they tag that branch (on Github) with a note saying "This branch passes." This is very helpful in code review.

They also test master and can automatically deploy new commits from it when all the tests are green. The idea is, never deploy manually. Let your testing service do that for you only when all your tests pass.

Using one of these services will ensure that a) all your tests get run, and b) you never ship broken code.

I've seen two main options around:

  1. Semaphore
  2. RailsOnFire

Automated Code Review with Code Climate

Code Climate automatically checks your ruby code for any duplication, code smells and complexity. It grades your apps and every class in your apps with a letter grade, giving you great insight into what code should get refactored.

3. Establish a Code Review Process

Your testing won't do much good if no one is making sure that the tests are passing. You need a code review process, and I suggest the following:

Branches

Features: All major features are developed in branches, prefixed with feature_. When a feature is done, the owner of the branch should create a pull request from that branch into master.

Bugs: Tiny bugs can be fixed on master. Every reasonably big bug should be:

  1. Recorded in your code host's issue tracker (Github's is really nice)
  2. Fixed in a new branch titled bugfix_issue_234 (where 234 is the number of the issue)
  3. Every commit should include "#234" in the title. This associates the commit with the issue.
  4. When the bug is fixed, a pull request should be opened into master.

Meaningful Code Review

Assign one of your team members per project to be responsible for code review. They are responsible to:

  1. Merge pull requests into master, after
  2. Examining the code for quality (Code Climate can help with this)
  3. Make sure the tests are passing (Automated services can do this for you, see above)
  4. Deploy the code. (Automated services can also do this for you)

In a perfect world, ONLY this team member is allowed to merge pull requests. This can be accomplished with permissions on the repo.

4. Read these articles

More articles on the way.

@jamonholmgren
Copy link

This looks fantastic. Thanks again for doing it! I'm emailing it to all the guys now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment