Skip to content

Instantly share code, notes, and snippets.

@Dawenster
Last active December 15, 2015 16:19
Show Gist options
  • Save Dawenster/5288172 to your computer and use it in GitHub Desktop.
Save Dawenster/5288172 to your computer and use it in GitHub Desktop.
Test-Driven Development Notes

Philosophy of testing

Intro

TDD is an approach where you write a test before you write just enough production code to fulfill that test and then refactor. Red-green-refactor is where you get a test to fail first, then you write code to get it to pass, then you refactor your code. Then repeat! :) Check out: http://betterspecs.org/

You should "test with a purpose" and know why you are testing something and to what level it needs to be tested. An interesting side effect of TDD is that you achieve 100% coverage test – every single line of code is tested – something that traditional testing doesn’t guarantee (although it does recommend it).

In general:

  • Tests should be reliable
  • Tests should be easy to write
  • Tests should be easy to understand

Happy vs Unhappy

An example of a happy path would be testing that a user gets created correctly. An unhappy path would be testing what happens when a user enters something he/she is not supposed to (e.g. bad email) and how the system handles it.

Unit tests vs. Integration tests

A unit test should have no dependencies on code outside the unit tested. You decide what the unit is by looking for the smallest testable part. Where there are dependencies they should be replaced by false objects.

An integration test is done to demonstrate that different pieces of the system work together. Integration tests cover whole applications.

Getting setup/started

Gemfiles

Typically Rails users use the RSpec gem to do their testing (as opposed to the default Rails test suite). Common gems that are used in the gemfile are listed below:

group :development, :test do
  gem 'rspec-rails'           # RSpec is the general testing framework that most Rails users use
  gem 'factory_girl_rails'    # Ensures that your test suite is always synced with the latest data
                              # model structure
end
group :test do
  gem 'faker'                 # fakes data very well
  gem 'capybara'              # mimics a user visiting your site and clicking on stuff
  gem 'guard-rspec'           # automatically and intelligently launch specs when files are modified
  gem 'launchy'               # launches the test view in your browser upon failed integration specs
end

'Rspec-rails' and 'factory_girl_rails' are used in both the development and test environments. The remaining gems are only used when you actually run your specs, so they’re not necessary to load in development. This also ensures that gems used solely for generating code or running tests aren’t installed in your production environment when you deploy to your server.

Factories

It is highly recommended that you have one factory for each class that provides the simplest set of attributes necessary to create an instance of that class. If you're creating ActiveRecord objects, that means that you should only provide attributes that are required through validations and that do not have defaults. Other factories can be created through inheritance to cover common scenarios for each class.

BAD!

user = User.create(
  name: 'Genoveffa',
  surname: 'Piccolina',
  city: 'Billyville',
  birth: '17 Agoust 1982',
  active: true
)

GOOD!

user = FactoryGirl.create :user</pre></code>

And in the 'spec/factories/user.rb' file...

<pre><code>require 'faker'
FactoryGirl.define do
  factory :user do |f|
    f.name { Faker::Name.first_name }
    f.surname { Faker::Name.last_name }
    f.city { Faker::City.name }
    f.birth { Faker::Date.date }
  end
end

Testing!

Vocabulary

  • describe - this indicates the component that you are testing - RSpec doesn't care what you put here - it is for humans to read; it wraps a set of tests against one functionality
  • it - this is for readability as well - helps to spell out what it is your test is testing for; often includes the word 'should' / 'should_not'
  • visit - uses Capybara to simulate visiting a particular route in your app
  • context - technically just an alias for 'describe'; contextually, we use it to wrap a set of tests against one functionality under the same state.
  • let - sets variables - allows for it to be defined outside the actual 'it' block for better readability
  • stub - method with canned response
  • mock - object with canned response

Examples

General layout:

describe <example group> do
  it <code example> do
    <subject code> 
    <expectation about subject code> 
  end
end

Simple usage of checking for content:

require 'spec_helper'
describe "Static pages" do
  describe "Home page" do
    it "should have the content 'Sample App'" do
      visit '/static_pages/home'
      page.should have_content('Sample App')
    end
  end
end

Slightly longer example:

describe "launch the rocket" do
  before(:each) do
    #prepare a rocket for all of the tests
    @rocket = Rocket.new
  end
  context "all ready" do
    before(:each) do
      #under the state of ready
      @rocket.ready = true
    end
    it "launch the rocket" do
      @rocket.launch().should be_true
    end
  end
  context "not ready" do
    before(:each) do
      #under the state of NOT ready
      @rocket.ready = false
    end
    it "does not launch the rocket" do
      @rocket.launch().should be_false
    end
  end
end

Using 'let' and other variables:

describe Card do
  subject do
    Card.new(card_type)
  end
  describe "#value" do
    context "Two of Hearts" do
      let(:card_type) { "2H" }
      its(:value) { should == 2 }
    end
    context "Bad Value" do
      it "should raise StandardError" do
        expect { card = Card.new("ZZ") }.to raise_error(StandardError)
      end
    end
  end
end

Mock & stub:

x = mock_model(X, :greet => 'hello')

Sources

http://net.tutsplus.com/tutorials/ruby/ruby-for-newbies-testing-with-rspec/
http://rubydoc.info/gems/rspec-core/frames
https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md
http://lmws.net/describe-vs-context-in-rspec
http://benscheirman.com/2011/05/dry-up-your-rspec-files-with-subject-let-blocks/
https://gist.github.com/enocom/3bd7c6f9e91c6a83f00a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment