问题描述
我有一个方法depart(plane)
,如果@weather.stormy?,它会出现错误fail飞机无法起飞,因为有暴风雨".这是在我的
airport class
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
我也有飞机课:
class Plane
end
方法 .stormy?
是一种生成随机数的方法.如果数字大于 75,则生成风暴,否则为 false.这是在我的 weather class
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
如果@weather.stormy?使用RSpec,我正在尝试测试错误fail飞机无法起飞,因为它是暴风雨".我发现这非常困难,因为我对 RSpec 非常陌生.
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?
我的整个 airport_spec.rb 文件:
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
失败的测试:
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
为什么我得到:
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
我再跑一次:
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
推荐答案
当您说 allow(Airport.new).to receive...
时,这是在设置新的 Airport
,not 指的是您使用 let
设置的那个.
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
.
stormy?
方法在 Weather
上,所以期望应该是:
The stormy?
method is on Weather
so the expectation should be:
allow(weather).to receive(:stormy?).and_return true
并设置测试对象以使用模拟的 stormy?
方法使用天气:
and set up the test subject to use the weather with the mocked stormy?
method:
let(:weather) { double :weather }
subject { Airport.new(weather) }
作为补充说明,receive
使用将被调用的方法的名称.当您编写 receive(weather.stormy?)
时,RSpec 正在尝试从 weather.stormy?
(这将是 True
或 False
) 转换成一个符号用作方法名称,因此 undefined method
to_sym'`.
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 method
to_sym'`.
查看您的规范的完整版本,您目前是一个隐式主体,即基于 describe Airport...
RSpec 创建了一个 Airport
使用 Airport.new
(不带参数)作为规范的主题.这意味着主题没有使用 weather
对象,你在其中存根了 stormy?
方法,所以你真正的 stormy?
方法是仍在使用.这就是为什么我使用 subject { Airport.new(weather) }
设置一个 显式主题.最后,您不想在测试中设置本地 weather
(weather = Weather.new
),因为您想要 allow(weather)...
引用传递给测试对象的 let
中的天气.
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
这篇关于异常 异常 RSpec的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!