Testing fundamentals

and

Introduction to Rspec

@h6165

The Fundamentals

The fundamentals: why test?

  • Verification : Does this feature work?
  • Spotting regression: Something broke something else 
  • Specification: The machine should work this way 
  • Documentation: Test describes how it should behave 
  • Developer feedback:
    • ​What I thought and what client wanted are different 
    • What I thought will not work
  • ​Code design: Decoupling, cleaner interfaces, better OOP 

The fundamentals: why test?

  • Manual tests are the default solution to the problem
  • Is also important, and unavoidable (UI/aesthetics)
  • Easy to get started
  • Economical: 
    • ManualTester.price < Developer.price (generally)

Manual Tests

Semi automated tests

  • Partially automated, needs some human involvement.
  • Record and playback / Export and compare
  • Not as repeatable as fully automated tests

The fundamentals: who writes the tests?

  • The developer

  • Testers/QA should write tests is an outdated idea, not practiced in Ruby world 

The fundamentals: when to start?

When to start testing

  • As early as possible

  • No tests are fine for very few situations, like prototype level or experimental projects

When to absolutely start testing

  • When the following signs show-up:

    • ​Regressions

    • Implementation slowness

    • Poor quality in general

    • Developer stress

The fundamentals: TDD

  • Test first, then code

  • Needs specifications beforehand, as examples
  • Ideal when specs are available (requirements)
  • Leads to good design of code
  • This talk/workshop is not about TDD
  • This may be a stepping stone towards TDD

The fundamentals: Test environment

  • Rails comes with a test environment.

  • The idea is: the database should be separate

  • And some settings/config/gems should also be separate

  • Test scripts are programs written as part of the same code base

  • The scripts are executed using a test runner

  • Test scripts contain a bunch of test-cases

  • Every test-case undergoes a setup and a teardown phase

  • Data creation/setup needed for the test-case happens in the setup-phase

  • Teardown is for cleanup, deletes all data created by the test

  • (Ideally) each test-case runs in complete isolation, and should not be dependent on any other tests

The fundamentals: Minitest and RSpec

  • Rails ships with the MiniTest test framework. RSpec is a popular alternative

  • All Rails project get MiniTest by default, and can be replaced with RSpec

  • The default data creation technique in Rails is fixtures, while the popular one is factories.

  • The difference has been a subject of much debate

  • We will cover RSpec for this workshop. Switching between the two is not very difficult after knowing the fundamentals

The fundamentals: TDD controversy

  • There is also a debate over whether TDD is the correct way to approach software development

  • Rails creator David H Hansen has written articles opposing, and there have been several in-depth arguments from both sides

  • It is worth looking into the entire discussion before arriving at a decision

RSpec Introduction

Rspec introduction

 

  • It is a test framework and a test runner
  • We write test-cases using Rspec recommended syntax
  • For verification, we use Rspec provided assertion methods
  • We create data on our own (or using factories or fixtures) – Rspec has no involvement there 
  • RSpec gives a mocking framework too

 

Rspec introduction: the test case

## Code under test:

def add(a, b)
  a + b
end


## The test

RSpec.describe 'add' do                ## mention what is being tested

  it 'should give 5 for 2 and 3' do    ## The test case
    c = add(2, 3)                      ## Invoke the code
    expect(c).to eq(5)                 ## Verify the result
  end
end

Rspec introduction: primary syntax

  • There are many more methods and concepts, but this is enough to get started
it:

Holds the test case

 

expect..to..eq

The assertion statement. Does the verification

 

describe

Wraps the test cases, and declares what is being tested

Rspec introduction: running the tests

  • Just one necessity: code under test should be loaded (require) before the describe block starts
  • The commend to run the test: 
    • ​​​ rspec test_file.rb 

Rspec Introduction: samples-1

  • The above code can be placed in a file, and be run using the rpsec command.
  • Will work if rspec gem is installed
## Tautology

require 'rspec'

RSpec.describe 'the truth' do

  it 'should be true' do
    expect(true).to eq(true)
  end

  it 'should not be false' do
    expect(true).to_not eq(false)
  end
end


## rspec sample1.rb

Rspec Introduction: samples-2

    ## Addition

    def add(a, b)
      a + b
    end


    ## The test
    require 'rspec'

    RSpec.describe 'add' do

      it 'should give 5 for 2 and 3' do
        c = add(2, 3)
        expect(c).to eq(5)
      end
    end


    ## rspec sample2.rb

Rspec Introduction: samples-3

## prime_number.rb
class PrimeNumber
  def self.is_it?(num)
    (2...num).each { |x| return false if num % x == 0 }
    true
  end
end

## prime_number_spec.rb
require 'rspec'
require File.expand_path('../prime_number.rb', __FILE__)

RSpec.describe 'PrimeNumber' do
  describe 'is_it?' do

    it 'should be true for 5' do
      expect(PrimeNumber.is_it?(5)).to eq(true)
    end

    it 'should be false for 77' do
      expect(PrimeNumber.is_it?(77)).to eq(true)
    end
  end
end

RSpec: common conventions

  • Test code is kept in the spec directory
  • There is generally a spec_helper.rb file that loads the necessary code and any plugins or even configs
  • The .rspec file contains some config too

Introduction to Rspec

By Abhishek Yadav

Introduction to Rspec

  • 1,209