Skip to content

Lecture 04

fluf1024 edited this page Nov 19, 2013 · 15 revisions

Testing in Ruby

What is testing?

  • automation
  • validation
  • specification

Why testing?

  • dynamic language
  • avoiding regressions
  • specifying requirements
  • communication
  • learning

Test Driven Development

  • write tests first
  • FAIL
  • PASS
  • REFACTOR
class Num
  def +(other)
    return 3
  end
end

# ...

def test_add
  x, y = Num.def(1), Num.def(2)
  assert_equal Num.def(3), x + y
end
class Num
  def +(other)
    self.value + other.value
  end
end

# ...

def test_add
  x, y = Num.def(1), Num.def(2)
  assert_equal Num.def(3), x + y
  assert_equal Num.def(4), y + y
end

xUnit frameworks

class UserTest < Test::Unit::TestCase
  def setup
    @neutrino = Neutrino.new
  end

  def test_run
    assert_raise EinsteinError do
      @neutrino.run(2 * LIGHT_SPEED)
    end
  end

  def teardown
    @neutrino.slow_down!
  end
end

xUnit drawback

  • orientation on code unit

Behaviour Driven Development

  • write specifications first
  • what it should do?
  • instead of does it work?
  • orientation on behaviour

RSpec

RSpec is a BDD (Behaviour-Driven Development) tool written in Ruby. It was created by Steven R. Baker and now is maintained mainly by David Chelimsky. It is an alternative to Test::Unit (RSpec is build as an extension of this one) included in Ruby and their purpose is quite similar. The code written using RSpec is called spec that contain example(s). Both words (spec and example) are synonyms for test.

  • BDD alternative to xUnit
  • should instead of assert
  • nesting
describe Passport do
  it 'is valid' do
    Passport.new.valid?.should be_true # alternative option (and much better) is Passport.new.should be_valid
  end
end

The whole code is spec and example is the part starting with it.

Example

describe Neutrino do
  # In this case RSpec creates automatically subject with an instance of the described class by default.
  # So we can miss this code normally
  subject { Neutrino.new }

  after { subject.slow_down! }

  it "should not run too fast" do
    lambda do
      subject.run(2 * LIGHT_SPEED)
    end.should raise_error EinsteinError
  end
end

Expectations

x.should eq 5             # passes if x == 5
x.should be               # passes unless x.nil?
x.should_not be           # passes if x.nil?
x.should be_empty         # passes if x.empty?
x.should_not be_blank     # passes unless x.blank? <- blank? is only in Rails
x.should have(5).items    # passes if x.size == 5
x.should be_a String      # passes if x.is_a? String
x.should match /string/   # passes if x =~ /string/

Nesting

describe Sound do
  context "in air" do
    before { subject.env = :air }

    its(:speed) { should eq SPEED_OF_SOUND }
  end

  context "in water" do
    before { subject.env = :water }

    its(:speed) { should > 4 * SPEED_OF_SOUND }
  end
end

Documentation

Sound
  in air
    speed
      should eq 343.2
  in water
    speed
      should > 1372.8

Cucumber

  • BDD for acceptance tests
  • Gherkin - business readable and domain specific language
  • Orientation on the communication with stakeholders, focusing on the problem domain

Narrative

Structure
As <role>
I want to <feature>
So that <business value>
Examples
As a manager
I want to create pie-chart
So that I can show it at the annual meeting.

# Similar way in Cucumber
In order to show pie-chart at the annual meeting   <business value>
As a manager                                       <role>
I want to create it                                <feature>

Scenarios

Scenario: pie-chart generating
  Given I have a spreadsheet:
  | Developers | LOC/day |
  | 1          | 1300    |
  | 2          | 1900    |
  | 3          | 1750    |
  When I choose to generate a pie-chart
  Then I should get a picture with circle
  And it's parts should correspond to the data

Step definitions

Given

  • indicates something that we accept to be true in a scenario
  • Given I have a car in my garage

When

  • indicates the event in a scenario
  • When I open my garage

Then

  • indicates an expected outcome
  • Then I should see the car in my garage

Example

Given "I have a spreadsheet" do |data|
  @spreadsheet = Spreadsheet.new(data.rows)
end

When /^I choose to generate a (.*)$/ do |chart_type|
  @chart = @spreadsheet.generate_chart(chart_type)
end

Then "I should get a picture with circle" do
  @chart.should be_like :circle
end

Tags

@stub-mail
Scenario: reset-password
Before("@stub-mail") do
  Mailer.stub(:send)
end

Why should we test?

"Because we are people"