Pregunta

Good evening,

I'm trying to test a fairly long method in my "Simulation" class, which calls class methods "is_male_alive?" and "is_female_alive?" on my "Age" class a few hundred times. The return values of these class methods are based on statistics, and I would like to stub them out to return specific values so that my tests run the same each time.

Age.rb:

...

def is_male_alive?(age)
  return false if age > 119
  if (age < 0 || age == nil || age == "")
    return false
  end    
  death_prob = grab_probability_male(age)
  rand_value = rand
  rand_value > death_prob
end

...

(the female version is essentially the same with some different constants)

In my "simulation" class I do the following:

def single_simulation_run

  ...
  male_alive = Age.is_male_alive?(male_age_array[0])
  female_alive = Age.is_female_alive?(female_age_array[0])
  ...
end

On each iteration of the simulation - essentially it just passes in an age (e.g. is_male_alive?(56) ) and returns true or false.

I'd like to stub out these two methods so that:

  1. is_male_alive? returns true for any argument less than 75, false otherwise
  2. is_female_alive? returns true for any argument less than 80, false otherwise

I've tried the following to see if I have the ability to stub it out (simulation_spec.rb) :

Age.should_receive(:is_male_alive?).exactly(89).times
results = @sim.send("generate_asset_performance")

But I get the following error:

 Failure/Error: Age.should_receive(:is_male_alive?).exactly(89).times
   (<Age(id: integer, age: integer, male_prob: decimal, female_prob: decimal) (class)>).is_male_alive?(any args)
       expected: 89 times
       received: 0 times

I also have no idea how to set it up so that the stubbed return value is dynamically generated based on the arguments. Is there a way to do this with a proc?

Is there a way to mock the entire Age class (as opposed to just mocking a single instance of the Age class?)

Thanks for your help!!

UPDATE 1

Looks like there is an issue with this method being called... which is really confusing. To really see if it was being called, I threw a "raise ArgumentError" into the methods.

Development environment (console):

1.9.3p125 :003 > sim = Simulation.last
1.9.3p125 :004 > sim.generate_results
  --->  ArgumentError: ArgumentError

So it is clearly calling this method in the development environment, as it threw the argumenterror.

Ran this again in my tests, and it's still saying that the method was not being called... I'm using your code below:

Age.should_receive(:is_male_alive?).with(an_instance_of(Fixnum)).at_least(:once) { |age| age < 75 }

I have also tried this

Age.should_receive(:is_male_alive?).with(an_instance_of(Fixnum)).at_least(:once) { raise ArgumentError }

Any thoughts?

¿Fue útil?

Solución

You can use blocks. See Arbitrary Handling in the message expectations documentation of rspec: http://rubydoc.info/gems/rspec-mocks/frames

Age.should_receive(:is_male_alive?).with(an_instance_of(Fixnum)).at_least(:once) { |age| age < 75 }
Age.should_receive(:is_female_alive?).with(an_instance_of(Fixnum)).at_least(:once) { |age| age < 80 }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top