Property based testing in Ruby

Abhishek Yadav

@h6165

Property based testing

  • What is it ?
  • Code examples
  • ??
  • Profit

Property based testing

  • Most testing we do in Ruby is example based
    (Rspec tests are called examples)
  • We try to come up with examples that cover the various usage scenarios. And we verify with literal results.
  • We generally don't go by the properties
  • All possible cases are often not covered, obviously
# Example of examples


def add(a, b)
  a + b
end


# Test examples -

expect(add(1,2)).to eq(3)
expect(add(0,0)).to eq(0)
expect(add(-1,1)).to eq(0)

# and so on...

Property based testing

That seems ok

# Pythagoras


def hypotenuse(a, b)
  Math.sqrt(a**2 + b**2)
end


# Test examples -

expect(hypotenuse(3, 4)).to eq(5.0)
expect(hypotenuse(5, 7)).to eq(8.6)
expect(hypotenuse(0, 0)).to eq(0)
expect(hypotenuse(-1, -2))
  .to raise_error

# and so on...

Property based testing

Here,

we desperately feel the urge to express the Pythagoras theorem

 

To say, that the square of the hypotenuse equals the sum of the squares of the sides

 

That, is a property

# Pythagoras


def hypotenuse(a, b)
  Math.sqrt(a**2 + b**2)
end


# What we need - 

for_all_possible_values {
  h = hypotenuse(a,b)
  expect(h**2).to eq(a**2 + b**2)
}

Property based testing

The square of the hypotenuse equals the sum of the squares of the sides

 

Property based testing

Haskells Quickcheck

  • Haskell folks built a library for just this
    (and published a couple of papers too)
  • What the library does -
    • Help generate a random test data
    • Help express the properties
    • Shrink the failure cases
  • Quickcheck - http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html
  • Quickcheck has been ported to most languages
    https://en.wikipedia.org/wiki/QuickCheck

Property based testing

Ruby's Quickcheck

  • Ruby has a few implementations -
    •  rubycheck - a direct port
        https://github.com/mcandre/rubycheck

    • theft -

        https://rubygems.org/gems/theft
    • rantly
        https://github.com/rantly-rb/rantly

Property based testing

Ruby's Rantly

# Pythagoras
# Property Test -

it 'hypotenuse squared is sum of sides squared' do

  property_of {
    a = integer
    b = integer
    [a, b]
  }
  .check(200) { |a, b|
    h2 = a**2 + b**2
    h2x = hypotenuse(a, b) ** 2
    expect(h2x).to eq(h2)
  }


end

Property based testing

Ruby's Rantly

  property_of {
    a = integer
    b = integer
    [a, b]
  }

Generate random test data

.check(200) { |a, b|
  h2 = a**2 + b**2
  h2x = hypotenuse(a, b) ** 2
  expect(h2x).to eq(h2)
}

Verify our function using the test data

(with 200 different data points)

Property based testing

Ruby's Rantly

.check(200) { |a, b|
  h2 = a**2 + b**2
  h2x = hypotenuse(a, b) ** 2
  expect(h2x).to eq(h2)
}

Does that feel a bit wrong  ?

 

We've almost specified the implementation here

Is that acceptable ?

Why or why not ?

 

Property based testing

Ruby's Rantly

Demo

Property based testing

Conclusion - Example tests vs Property tests

  • Property tests are more useful with algorithms, formulae etc, where -
    • there are lots of test values
    • there's a risk of missing edge cases
    • functions are pure
  • Example unit tests - more useful with business logic, where code is mostly impure

Property based testing

Conclusion - Example tests vs Property tests

That's all

Property based testing

By Abhishek Yadav

Property based testing

  • 1,009