2

I am very new to Rspec and was trying to test a Ruby script (Its a Chef recipe) with the following skeleton.

def foo1
   # do stuff
   list_of_names  # returns a list
end

def foo2(list_of_names)
  # do stuff
  counter = [...]
  .
  .
  counter_by_name_hash   # returns a hash
end

def main
  list = foo1
  counter_by_name_hash = foo2(list)
end

main

I want to assert that the function foo2 returns an empty hash when an empty list is passed as argument and a non-empty hash when a valid non empty list is passed to it.

I tried the following two ways:

First: Here I get the NoMethodError - undefined method foo2 error. Not sure how to declare subject since the method foo2 is not within a class, its in a Ruby script which runs as a Chef recipe.

it 'has valid list of names as input and returns valid hash' do
    valid_hash = {...}
    expect(subject.foo2(valid_list_of_names)).to eq(valid_hash)
end

Second: Not sure if this is asserting the return value since it does not throw error even when I specify an unexpected value in the and_return part.

it 'has valid list of names as input and returns valid hash' do
    valid_hash = {...}
    expect(subject).to receive(:foo2).with(valid_list_of_names).and_return(valid_hash)
    subject.foo2(valid_list_of_names)
end

My question really is that what is the correct way to assert the return value of the function foo2 and how should I declare Subject in my spec file if the method foo2 is not within a Ruby class but in a Recipe? Any input or help is appreciated.

ka2010
  • 98
  • 1
  • 9

1 Answers1

1

EDIT:

I'm not familiar with Chef, but you can require a file from your repository in an rspec test and access the methods therein. Like so:

# path/to/chef_file
def foo1
   # do stuff
   list_of_names  # returns a list
end

def foo2(list_of_names)
  # do stuff
  counter = [...]
  .
  .
  counter_by_name_hash   # returns a hash
end

def main
  list = foo1
  counter_by_name_hash = foo2(list)
end


# spec/chef_tests/my_test.rb
require 'path/to/chef_file'

describe 'Chef Tests' do
  before do
    valid_list_of_names = [...]
    valid_hash = {...}
  end

  it 'foo1 returns a valid list of names' do
    expect( foo1 ).to eq valid_list_of_names
  end

  it 'foo2 returns a valid_hash with a valid_list_of_names as input' do
    expect(foo2(valid_list_of_names)).to eq(valid_hash)
  end

end

First Answer:

It looks like you are using a singleton pattern for you class. In which case, your class and tests could look like this and it should work.

class SomeClass
  class<<self
    def foo1
       # do stuff
       list_of_names  # returns a list
    end

    def foo2(list_of_names)
      # do stuff
      counter = [...]
      .
      .
      counter_by_name_hash   # returns a hash
    end

    def main
      list = foo1
      counter_by_name_hash = foo2(list)
    end
  end
end

describe SomeClass do
  before do
    valid_list_of_names = [...]
    valid_hash = {...}
  end

  it 'has valid list of names as input and returns valid hash' do
    expect(SomeClass.foo2(valid_list_of_names)).to eq(valid_hash)
  end

  it 'has valid list of names as input and returns valid hash' do
      expect(SomeClass).to receive(:foo2).with(valid_list_of_names).and_return(valid_hash)
      SomeClass.foo2(valid_list_of_names)
  end
end

Obviously, I'm making an assumption here, but if you are using a more stand ruby-way of having a instance, then you'd just initialize the object with @my_instance = SomeClass.create(...) and then check your methods off of the instance. Like this:

expect( @my_instance.foo2( valid_list_of_names ) ).to eq valid_hash
James Milani
  • 1,921
  • 2
  • 16
  • 26
  • Since this Ruby script is a Chef Recipe, I cannot change it to a class. Is there another way of accomplishing this. – ka2010 Feb 26 '17 at 02:31