Emilio Menéndez Emilio Menéndez - 7 months ago 11
Ruby Question

Calling a method form a different class ruby

I am just starting with Ruby and Rspec, so if I am not very specific I apologise for my bad syntax in advance.

I have two classes, Airport and Weather, that should communicate so I can call the

stormy?
method in Weather from Airport. What I want to do is stop planes from taking off when the weather is bad.
I have defined a
bad_weather
method but it doesn't work
Here is my code:

My test in Rspec

describe Airport do
it ' does not allow planes to take off with stormy weather' do
subject.take_off double(:plane)
expect {(weather).to be_stormy?}.to raise_error "Flight cancelled due to bad weather"
end
end


The class I want to take the method from

class Weather

def stormy?
roll >= 6
end

private

def roll
rand(10)
end
end


And the class I want to call the method in

class Airport

DEFAULT_CAPACITY = 10

def initialize
@planes = []
@capacity = DEFAULT_CAPACITY
end


def take_off(plane)
fail "Flight cancelled due to bad weather" if bad_weather?
@planes.pop
end

def bad_weather?
weather = Weather.new
weather.stormy?
end
end


I know my Rspec test in lousy, any help would be appreciated.

Answer

When you have this situations where your subject under test is relying on other objects, you want to have control on those objects. In your scenario, you want to control the weather that your airport knows about.

In order to do that, you need to force it to make that when you ask stormy? to it, it returns true. That way you are ensuring you are unit testing your Airport class and all the dependencies on other objects are under control.

Here is how I would do this:

class Airport
   DEFAULT_CAPACITY = 10

   def initialize
     @planes = []
     @capacity = DEFAULT_CAPACITY
   end

  def take_off(plane)
    fail "Flight cancelled due to bad weather" if bad_weather?
    @planes.pop
  end

  def bad_weather?
    weather.stormy?
  end

  def weather
    @weather ||= Weather.new
  end
end

And then in your test, you are going to control what weather your airport has by doing this:

it 'does not allow planes to take off with stormy weather' do
  my_airport = Airport.new
  stormy_weather = Weather.new
  allow(stormy_weather).to receive(:is_stormy?) { true }
  allow(my_airport).to receive(:weather) { stormy_weather }

  expect(my_airport.take_off("Boeing")).to raise_error "Flight cancelled due to bad weather"
end

@SteveTurczyn answer is also valid. I personally don't like it because you are not just making the weather of your airport stormy, but any Weather instance.