Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing hashes in RSpec that contain BigDecimal

Tags:

ruby

hash

rspec

I am using RSpec (3.x) to test an object that acts similar to a calculator. The object puts the results into a hash. But I cannot get the hash to match correctly in my tests. Here is an example of what I'm talking about:

class ObjectUnderTest

  def calculate(a, b)
    value = a.to_d / b
    {
      value: value
    {
  end

end

The test looks like this:

RSpec.describe ObjectUnderTest do

   it "calculates the product of two values" do
     o = ObjectUnderTest.new
     expect(o.calculate(1, 3)).to eql({value: 0.333})
   end
end

The problem is that 0.333 is a float and the actual value being included in the hash is a BigDecimal. If I change the line in the test to say something like:

expect(o.calculate(1, 3)).to eql({value: 0.333.to_d(3)})

... the test still fails A) because the precision is different, and B) in my actual code, I have several key-value pairs and I don't want to have to call k.to_d(some_precision) on all of the comparison hashes to get it to pass.

Is there a way to compare the values using something like a_value_with some range do I don't have to hard-code an exact number in there?

like image 215
Mike Farmer Avatar asked Jun 29 '15 22:06

Mike Farmer


1 Answers

Floating point numbers are inexact (even to the point that (0.1 + 0.2) == 0.3 returns false), so you have to use matchers that allow for values near to your expected value rather than equal. RSpec's be_within(x).of(y) (and its alias, a_value_within(x).of(y)) is designed for matching floating point numbers. RSpec 3 also supports composable matchers and the match matcher allows you to match nested hash/array data structures by putting matchers in place of values, so you can do this:

expect(o.calculate(1, 3)).to match(value: a_value_within(0.001).of(0.333))
like image 121
Myron Marston Avatar answered Nov 20 '22 20:11

Myron Marston