RSpec 测试依赖于 returns 随机数的模块方法的请求
RSpec testing a request that depends on a module method that returns a random number
我在 Rails api 中有以下模块:
module SpeedCalculator
def self.adjustment
Random.rand(0..0.3)
end
def self.calculate_adjustment(track, car_speed)
case track.surface_type
when "asphalt"
car_speed - (car_speed * self.adjustment).ceil
when "gravel"
car_speed - (car_speed * self.adjustment).ceil
when "snow"
car_speed - (car_speed * self.adjustment).ceil
else
car_speed
end
end
end
我可以成功测试调整方法是这样的:
require 'rails_helper'
RSpec.describe SpeedCalculator do
include SpeedCalculator
it "uses an adjustment value between 0 and 0.3" do
expect(SpeedCalculator.adjustment).to be >= 0
expect(SpeedCalculator.adjustment).to be <= 0.3
end
end
可以像这样发出 API 请求:
localhost:3000/api/v1/cars/porsche-911?track=摩纳哥
您要求系统计算给定赛道上给定汽车的速度的位置。
所以我需要编写一个请求规范,以针对给定的汽车和赛道返回正确的值。但是,当 calculate_adjustment 总是应用随机数时,我该怎么做呢?
我认为我需要为 self.adjustment 创建一个 mock/stub,所以测试应该是这样的:
it "calculates max_speed_on_track when a valid track name is provided" do
Car.create!(name:'Subaru Impreza', max_speed:'120', speed_unit:'km/h')
Track.create(name:'Monaco')
# here create a dummy version of adjustment
# that always returns a fixed value, rather than a random value
# and somehow use that dummy for the request?
# Since x below needs to be a known value for the test to work.
get api_v1_car_path(id: 'porsche-911', track: 'monaco')
expect(response).to have_http_status(200)
expect(response.body).to include_json(car: {max_speed_on_track: x})
end
how can I do that when the calculate_adjustment always applies a random number?
I believe that I need to create a mock/stub for self.adjustment
正是!您在测试中最不想要的是随机行为(和随机失败)。您想要可重现的结果。所以,是的,嘲笑你的 RNG。
最简单的做法是:
expect(Random).to receive(:rand).with(0..0.3).and_return(0.1234)
# or better
expect(SpeedCalculator).to receive(:adjustment).and_return(0.1234)
# then proceed with your test
get api_v1_car_path(id: 'porsche-911', track: 'monaco')
...
这里的进一步改进是不使用控制器规范来测试业务逻辑。将您的业务逻辑封装在一个对象中并测试该对象(您可以在其中充分使用依赖注入技术)。你的控制器会变成这样:
class CarsController
def show
calculator = MaxSpeedCalculator.new(params[:id], params[:track])
render json: calculator.call
end
end
我在 Rails api 中有以下模块:
module SpeedCalculator
def self.adjustment
Random.rand(0..0.3)
end
def self.calculate_adjustment(track, car_speed)
case track.surface_type
when "asphalt"
car_speed - (car_speed * self.adjustment).ceil
when "gravel"
car_speed - (car_speed * self.adjustment).ceil
when "snow"
car_speed - (car_speed * self.adjustment).ceil
else
car_speed
end
end
end
我可以成功测试调整方法是这样的:
require 'rails_helper'
RSpec.describe SpeedCalculator do
include SpeedCalculator
it "uses an adjustment value between 0 and 0.3" do
expect(SpeedCalculator.adjustment).to be >= 0
expect(SpeedCalculator.adjustment).to be <= 0.3
end
end
可以像这样发出 API 请求:
localhost:3000/api/v1/cars/porsche-911?track=摩纳哥
您要求系统计算给定赛道上给定汽车的速度的位置。
所以我需要编写一个请求规范,以针对给定的汽车和赛道返回正确的值。但是,当 calculate_adjustment 总是应用随机数时,我该怎么做呢?
我认为我需要为 self.adjustment 创建一个 mock/stub,所以测试应该是这样的:
it "calculates max_speed_on_track when a valid track name is provided" do
Car.create!(name:'Subaru Impreza', max_speed:'120', speed_unit:'km/h')
Track.create(name:'Monaco')
# here create a dummy version of adjustment
# that always returns a fixed value, rather than a random value
# and somehow use that dummy for the request?
# Since x below needs to be a known value for the test to work.
get api_v1_car_path(id: 'porsche-911', track: 'monaco')
expect(response).to have_http_status(200)
expect(response.body).to include_json(car: {max_speed_on_track: x})
end
how can I do that when the calculate_adjustment always applies a random number?
I believe that I need to create a mock/stub for self.adjustment
正是!您在测试中最不想要的是随机行为(和随机失败)。您想要可重现的结果。所以,是的,嘲笑你的 RNG。
最简单的做法是:
expect(Random).to receive(:rand).with(0..0.3).and_return(0.1234)
# or better
expect(SpeedCalculator).to receive(:adjustment).and_return(0.1234)
# then proceed with your test
get api_v1_car_path(id: 'porsche-911', track: 'monaco')
...
这里的进一步改进是不使用控制器规范来测试业务逻辑。将您的业务逻辑封装在一个对象中并测试该对象(您可以在其中充分使用依赖注入技术)。你的控制器会变成这样:
class CarsController
def show
calculator = MaxSpeedCalculator.new(params[:id], params[:track])
render json: calculator.call
end
end