user1438150
user1438150

Reputation: 175

Rspec Ruby Mocking

I would like to achieve 100% coverage on a module. My problem is that there is a variable (called data) within a method which I am trying to inject data in to test my exception handling. Can this be done with mocking? If not how can i fully test my exception handling?

module CSV
  module Extractor
class ConversionError   < RuntimeError; end
class MalformedCSVError < RuntimeError; end
class GenericParseError < RuntimeError; end 
class DemoModeError     < RuntimeError; end

def self.open(path)
  data = `.\\csv2text.exe #{path} -f xml --xml_output_styles 2>&1`
  case data
  when /Error: Wrong input filename or path:/
    raise MalformedCSVError, "the CSV path with filename '#{path}' is malformed"
  when /Error: A valid password is required to open/
    raise ConversionError, "Wrong password: '#{path}'"
  when /CSVTron CSV2Text: This page is skipped when running in the demo mode./
    raise DemoModeError, "CSV2TEXT.exe in demo mode"
  when /Error:/
    raise GenericParseError, "Generic Error Catch while reading input file"
  else
    begin
      csvObj = CSV::Extractor::Document.new(data)
    rescue
      csvObj = nil
    end
    return csvObj
  end  
end
end
end

Let me know what you think! Thanks

===================== EDIT ========================

I have modified my methods to the design pattern you suggested. This method-"open(path)" is responsible for trapping and raising errors, get_data(path) just returns data, That's it! But unfortunately in the rspec I am getting "exception was expected to be raise but nothing was raised." I thought maybe we have to call the open method from your stub too?

This is what I tried doing but still no error was raised..

    it 'should catch wrong path mode' do
    obj = double(CSV::Extractor)
    obj.stub!(:get_data).and_return("Error: Wrong input filename or path:")
    obj.stub!(:open)
    expect {obj.open("some fake path")}.to raise_error CSV::Extractor::MalformedCSVError
    end

Upvotes: 2

Views: 596

Answers (2)

FMc
FMc

Reputation: 42411

Extract the code that returns the data to a separate method. Then when you test open you can stub out that method to return various strings that will exercise the different branches of the case statement. Roughly like this for the setup:

def self.get_data(path)
  `.\\csv2text.exe #{path} -f xml --xml_output_styles 2>&1`
end

def self.open(path)
  data = get_data(path)
  ...

And I assume you know how to stub methods in rspec, but the general idea is like this:

foo = ...
foo.stub(:get_data).and_return("Error: Wrong input filename or path:")
expect { foo.get_data() }.to raise_error MalformedCSVError

Also see the Rspec documentation on testing for exceptions.

Upvotes: 1

samuil
samuil

Reputation: 5081

Problem with testing your module lies in the way you have designed your code. Think about splitting extractor into two classes (or modules, it's matter of taste -- I'd go with classes as they are a bit easier to test), of which one would read data from external system call, and second would expect this data to be passed as an argument.

This way you can easily mock what you currently have in data variable, as this would be simply passed as an argument (no need to think about implementation details here!).

For easier usage you can later provide some wrapper call, that would create both objects and pass one as argument to another. Please note, that this behavior can also be easily tested.

Upvotes: 0

Related Questions