Search code examples
rubyrspectddbdd

How to test a class method that modifies an attribute of another class in a containerised way rspec


I have an issue I have been whacking my head against for hours now, and neither I nor anyone I have asked has been able to come up with a suitable answer.

Essentially, I am writing a method that allows me to edit an instance variable of another method. I have multiple ways of doing this, however my issue is with writing the test for this method. I have tried many different double types, however as they are immutable and do not store states, I did not manage to find a way to make it work.

Here is the class whose working variable is changed:

class MyClass
  attr_writer :working

  def working?
    @working
  end
end

Here is the class and method that change it:

class OtherClass
  def makes_work
    @ary_of_instances_of_MyClass_to_fix.map do |x|
      x.working = true
      @ary_of_fixed_objects << x
    end
  end
end

(The actual class is much larger, but I have only included a generalised version of the method in question. I can put all of the specific code up in a gist if it would help)

So I need a way to test that makes_work does in fact accept the array of objects to be changed, changes them and appends them to array_of_fixed_objects. What would be the best way of testing this in a containerised way, without requiring MyClass?

My last attempt was using spies to see what methods were called on my dummy instance, however a range of failures, depending on what I did. Here is the most recent test I wrote:

describe '#make_work' do
  it 'returns array of working instances' do
    test_obj = spy('test_obj')
    subject.ary_of_instances_of_MyClass_to_fix = [test_obj]
    subject.makes_work
    expect(test_obj).to have_received(working = true)
  end
end

This currently throws the error:

undefined method to_sym for true:TrueClass

Many thanks for any help! I apologise if some formatting/ info is a little bit messed up, I am still pretty new to this whole stackoverflow thing!


Solution

  • I think the problem is have_received(working = true), it should be have_received(:working=).with(true)

    Edit:

    Examples of using have_received


    This works for me

    class MyClass
      attr_writer :working
    
      def working?
        @working
      end
    end
    
    class OtherClass
      attr_writer :ary_of_instances_of_MyClass_to_fix
    
      def initialize
        @ary_of_fixed_objects = []
      end
      def makes_work
        @ary_of_instances_of_MyClass_to_fix.map do |x|
          x.working = true
          @ary_of_fixed_objects << x
        end
      end
    end
    
    describe '#make_work' do
      subject { OtherClass.new }
      it 'returns array of working instances' do
        test_obj = spy('test_obj')
        subject.ary_of_instances_of_MyClass_to_fix = [test_obj]
        subject.makes_work
        expect(test_obj).to have_received(:working=).with(true)
      end
    end