2

In my code I have code similar to the following contrived example.

class Excel
  def self.do_tasks
    with_excel do |excel|
      delete_old_exports
      export_images(excel)
      export_documents(excel)
    end
  end

  def with_excel
    excel = WIN32OLE.connect('Excel.Application')
    begin
      yield excel
    ensure
      excel.close()
    end
  end
end

Now, I want to write a test for the 'do_tasks' method, where I set up expectations for the method calls and see if those expectations are fulfilled.

I tried the following approach (with shoulda-context and test-unit). However,the expectations fail for the three last mocks (the mocks do not get called).

class ExcelTest < ActiveSupport::TestCase  
  should "call the expected methods" do  
    mock.proxy(Excel).with_excel
    mock(Excel).delete_old_exports
    mock(Excel).export_images.with_any_args
    mock(Excel).export_documents.with_any_args

    Excel.do_tasks
  end
end

Any pointers on how to test this sort of code would be much appreciated!

tbys
  • 21
  • 1
  • Check out http://stackoverflow.com/a/19201353/449531 for a similar question and solution using rr. – zhon Oct 05 '13 at 19:00

1 Answers1

1

An older question, but I've just been doing some work on some similar code with rr and thought I'd throw in an answer.

The following test will do what you asked (using RR and TestUnit):

describe Excel do
  describe '.do_tasks' do
    let(:excel_ole) { mock!.close.subject }

    before do
      stub(WIN32OLE).connect('Excel.Application') { excel_ole }
      mock(Excel).delete_old_exports
      mock(Excel).export_images(excel_ole)
      mock(Excel).export_documents(excel_ole)
    end

    it 'calls the expected methods' do
      Excel.do_tasks
      assert_received(Excel) { |subject| subject.delete_old_exports }
    end
  end
end

It uses RR's "spy" doubles - see https://github.com/rr/rr#spies

However, in the case of the sample code you provided, the fact that the methods you want to test are inside a block is an implementation detail and shouldn't be implicitly tested (this can lead to brittle tests). The test above shows this, the with_excel method is not mocked (incidentally, this should be defined as self.with_excel for the code to work). The implementation could be refactored so that the WIN32OLE initialisation and teardown happens inline in the .do_tasks method and the test would still pass.

On another note, it may be a side effect of the contrived example, but in general it's a bad idea to test non-public methods. The methods delete_old_exports, export_images and export_documents look like they should perhaps be factored out to collaborators.

Jim Riordan
  • 1,378
  • 11
  • 18