Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing class which uses refinements with RSpec

Let's say I've got refinement

module RefinedString
  refine String do
    def remove_latin_letters
      #code code code code
    end
  end
end

and I use it inside my class Speech:

class Speech
  using RefinedString
  def initialize(text)
    @content = text.remove_latin_letters
  end
end

I've written tests for refinement in RSpec and now I'm testing Speech class

describe Speech
  let(:text) { "ąńńóyińg" }

  it 'should call my refinement' do
    expect(text).to receive(:remove_latin_letters)
    Speech.new(text)
  end
end

but I get RSpec::Mocks::MockExpectationError: "ąńńóyińg" does not implement: remove_latin_letter

I don't think mocking it is a good solution (but I may be wrong! Is mocking the solution here?)

so I tried

let(:text) { described_class::String.new("ąńńóyińg") } but the result is the same.

I don't want to explicitly call using RefinedString inside my RSpec (it should figure it out on its own, right?)

How to make RSpec aware of my refined methods?

like image 283
Filip Bartuzi Avatar asked Feb 10 '23 04:02

Filip Bartuzi


1 Answers

We always want to test behavior, rather than implementation. To my mind, refinements change the behavior of other classes by virtue of being included, rather than having their own behavior. To use a somewhat clumsy analogy, if we were to test the reproductive behavior of a virus, we would have to introduce it into a host cell. We are interested in what happens to the host when the virus takes over (so to speak).

One approach is to build test classes with and without the refinement, e.g.:

class TestClass
  attr_reader :content
  def initialize(text)
    @content = text.remove_latin_letters
  end
end

describe "when not using RefinedString" do
  it "raises an exception" do
    expect { TestClass.new("ąńńóyińg") }.to raise_error(NoMethodError)
  end
end

class RefinedTestClass
  using RefinedString
  attr_reader :content
   def initialize(text)
     @content = text.remove_latin_letters
  end
end

describe "when using RefinedString" do
  it "removes latin letters" do
    expect(RefinedTestClass.new("ąńńóyińg").content).to eq "ńńóń"
  end
end
like image 52
zetetic Avatar answered Feb 11 '23 18:02

zetetic