Exception Exception RSpec

℡╲_俬逩灬. 提交于 2019-12-25 01:45:54

问题


I have a method depart(plane) which takes an error fail "The plane can't set off because it is stormy" if @weather.stormy?. This is defined in my airport class

class Airport

  attr_accessor :planes, :landed, :weather

  def initialize(weather = Weather.new)
    #plane has no location when initialized

    @landed = nil
    @planes  = []
    @weather = weather

  end


  def land(plane)
    fail "You can't land this plane again!" if @landed == true
    @planes << plane
    @landed = true
  end

  def depart(plane)

    fail "The plane has already departed" if @landed == false
    fail "The plane can't set off because it is stormy" if @weather.stormy?
    @planes.pop
    puts "Your plane has left the airport!"
    @landed = false
    end
  end

I also have a plane class:

class Plane
end

The method .stormy? is a method in which a random number is generated. If the number is above 75, a storm is generated otherwise it is false. This is defined in my weather class

def stormy?
    number > 70 ? true : false
end

def number
    rand(1..100)
end

I am trying to test the error fail "The plane can't set off because it is stormy" if @weather.stormy?using RSpec. I am finding this extremely difficult as I am very new to RSpec.

The problem I am having is passing this test as the storm either can be true or false. How can I preset a value and test it?

My whole airport_spec.rb file:

require 'airport'
require 'plane'
require 'weather'

describe Airport do

  let(:airport) { Airport.new }
  let(:plane) { double :plane }
  let(:weather) { double :weather}
  #let(:weather) {double :weather}

  it 'creates new airports' do
    expect(:airport).to eq(:airport)
  end

  it 'accepts landed planes' do
    subject.land(:plane)
    expect(subject.landed).to be(true)
  end

 describe '#initialize' do
  it 'initializes a planes array when airport is instantiated' do
    expect(airport.planes).to be_an_instance_of(Array)
  end

  it 'initializes the plane to be landed to be nil upon instantiation' do
    expect(airport.landed).to be nil
  end

  it 'instantiates a new weather object upon initialization' do
    weather = airport.weather
    expect(airport.weather).to eq weather
  end
end

 describe '#land' do
   it 'adds a plane to the planes array when landed' do
    subject.land(:plane)
    expect(subject.planes).to eq [:plane]
  end

  it 'will not land a plane that is already landed' do

    subject.land(:plane)
    expect {subject.land(:plane)}.to raise_error("You can't land this plane again!")
  end
end

  describe '#depart' do

  it 'will not allow a plane to take off when it is stormy' do
   weather = Weather.new
   allow(weather).to receive(:stormy?).and_return true
   expect{subject.depart(plane)}.to raise_error("The plane can't set off because it is stormy")
   end
end

  describe '#full' do
    it 'will raise an error when the airport is too full' do
    expect(subject.full?).to eq(true)
    end
  end
end

The test that is failing:

    it 'will not allow a plane to take off when it is stormy' do
        weather = Weather.new
        allow(Airport.new).to receive(weather.stormy?).and_return true
        expect{subject.depart(plane)}.to raise_error("The plane can't set off because it is stormy")
    end
end

Why am I getting:

Failures:

  1) Airport#depart will not allow a plane to take off when it is stormy
     Failure/Error: expect{subject.depart(plane)}.to raise_error("The plane can't set off because it is stormy")
       expected Exception with "The plane can't set off because it is stormy" but nothing was raised
     # ./spec/airport_spec.rb:75:in `block (3 levels) in <top (required)>'

Finished in 0.02878 seconds (files took 0.15908 seconds to load)
12 examples, 1 failure

RSpec version : 3.5.4

Another time I run:

Failures:

  1) Airport#depart will not allow a plane to take off when it is stormy
     Failure/Error: expect{subject.depart(plane)}.to raise_error("The plane can't set off because it is stormy")
       expected Exception with "The plane can't set off because it is stormy" but nothing was raised
     # ./spec/airport_spec.rb:58:in `block (3 levels) in <top (required)>'

  2) Airport#depart removes a plane from the planes array when taken-off
     Failure/Error: fail "The plane can't set off because it is stormy" if @weather.stormy?

     RuntimeError:
       The plane can't set off because it is stormy
     # ./lib/airport.rb:20:in `depart'
     # ./spec/airport_spec.rb:63:in `block (3 levels) in <top (required)>'

Finished in 0.03361 seconds (files took 0.15734 seconds to load)
16 examples, 2 failures

Failed examples:

rspec ./spec/airport_spec.rb:56 # Airport#depart will not allow a plane to take off when it is stormy
rspec ./spec/airport_spec.rb:61 # Airport#depart removes a plane from the planes array when taken-off

However, sometimes it works and passes:

COVERAGE: 100.00% -- 76/76 lines in 6 files

Benjamins-MacBook-Pro:airport_challenge benjamin$ rspec

Airport
  creates new airports
  accepts landed planes
  #initialize
    initializes a planes array when airport is instantiated
    initializes the plane to be landed to be nil upon instantiation
    instantiates a new weather object upon initialization
  #land
    adds a plane to the planes array when landed
    will not land a plane that is already landed
  #depart
    will not allow a plane to take off when it is stormy
  #full
    will raise an error when the airport is too full

Plane
  creates new planes

Weather
  creates a weather object
  #number
    will create a random number
  #storm
    will either be stormy or sunny

Have you considered running rubocop? It will help you improve your code!
Try it now! Just run: rubocop

Finished in 0.01254 seconds (files took 0.15439 seconds to load)
13 examples, 0 failures


COVERAGE:  96.05% -- 73/76 lines in 6 files

+----------+----------------+-------+--------+---------+
| coverage | file           | lines | missed | missing |
+----------+----------------+-------+--------+---------+
|  83.33%  | lib/airport.rb | 18    | 3      | 25-27   |
+----------+----------------+-------+--------+---------+
5 file(s) with 100% coverage not shown

回答1:


When you say allow(Airport.new).to receive... this is setting up a new Airport, not referring to the one you've set up using let.

The stormy? method is on Weather so the expectation should be:

allow(weather).to receive(:stormy?).and_return true

and set up the test subject to use the weather with the mocked stormy? method:

let(:weather) { double :weather }
subject { Airport.new(weather) }

As a side explanation, receive takes the name of a method that will be called. When you write receive(weather.stormy?) RSpec is trying to convert the return value from weather.stormy? (which would be True or False) into a symbol to use as the method name, hence undefined methodto_sym'`.


Looking at the full version of your spec, you are currently an implicit subject i.e. based on the describe Airport... RSpec has created an Airport using Airport.new (with no arguments) to be the subject of your spec. This means that the subject isn't using the weather object where you've stubbed the stormy? method and so your real stormy? method is still being used. This is why I set up an explicit subject using subject { Airport.new(weather) }. Lastly, you don't want to be setting up a local weather in the test (weather = Weather.new) because you want allow(weather)... to refer to the weather from the let that's passed to the test subject.


Here's a complete version of the spec with the changes made:

require 'airport'

describe Airport do

  let(:weather) { double :weather }
  subject { Airport.new(weather) }
  let(:plane) { double :plane }

    it 'will not allow a plane to take off when it is stormy' do
      allow(weather).to receive(:stormy?).and_return true
      expect { subject.depart(plane) }.to raise_error("The plane can't set off because it is stormy")
    end

end


来源:https://stackoverflow.com/questions/46026166/exception-exception-rspec

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!