尝试通过向 rspec 传递特定数字来测试我的随机生成器

发布于 2025-01-10 09:01:52 字数 880 浏览 0 评论 0原文

我目前正在尝试为以下代码创建测试。我先编写了测试,然后编写了代码,然后意识到我的测试没有做我想要它做的事情。

class Weather

  def initialize
    @weather = 'sunny'
  end

  def weather_randomiser
    case rand(1..10)
    when 1..8 then @weather = 'sunny'
    when 9..10 then @weather = 'stormy'
    end
  end

  def stormy?
    weather_randomiser
    @weather == 'stormy'
  end
end

我的测试如下:

require 'weather.rb'

describe Weather do
  it 'randomises weather and sets the weather to the randomised result' do
    weather = Weather.new
    allow(weather).to receive(:weather_randomiser).and_return(9)
    expect(weather).to be_stormy
    allow(weather).to receive(:weather_randomiser).and_return(2)
    expect(weather).to_not be_stormy
  end
end

您能解释一下我如何测试weather_randomiser和stormy吗?与此类似的方法/我的代码是否可行?

您能否进一步解释一下您是如何到达您所做的事情的,因为我想了解而不仅仅是一个答案。

预先非常感谢您:)

I am currently trying to create a test for the below code. I wrote the test first and then wrote the code and realised my test is not doing what I want it to do.

class Weather

  def initialize
    @weather = 'sunny'
  end

  def weather_randomiser
    case rand(1..10)
    when 1..8 then @weather = 'sunny'
    when 9..10 then @weather = 'stormy'
    end
  end

  def stormy?
    weather_randomiser
    @weather == 'stormy'
  end
end

My test is as follows:

require 'weather.rb'

describe Weather do
  it 'randomises weather and sets the weather to the randomised result' do
    weather = Weather.new
    allow(weather).to receive(:weather_randomiser).and_return(9)
    expect(weather).to be_stormy
    allow(weather).to receive(:weather_randomiser).and_return(2)
    expect(weather).to_not be_stormy
  end
end

Can you please explain how I go about testing the weather_randomiser and stormy? methods similar to this/ is it possible with my code.

Further could you explain how you got to where you did as I would like to understand not just have an answer.

Thank you very much in advance :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

翻了热茶 2025-01-17 09:01:52

你的weather_randomiser做了太多的事情:

  1. 它生成一个随机数
  2. 它将一个随机数映射到一个字符串值
  3. 它更新一个实例变量

为了使事情更具可测试性,你可以将它们分开,例如:

def weather_randomiser
  rand(1..10)
end

def set_weather(value)
  @weather = case value
    when 1..8 then 'sunny'
    when 9..10 then 'stormy'
  end
end

def stormy?
  set_weather weather_randomiser
  @weather == 'stormy'
end

它确实引入了随机生成器和设置器之间的尴尬耦合;改变前者需要考虑后者...... ymmv

Your weather_randomiser does too many things:

  1. It generates a random number
  2. It maps a random number to a string value
  3. It updates an instance variable

To make things more testable, you could split those out, e.g.:

def weather_randomiser
  rand(1..10)
end

def set_weather(value)
  @weather = case value
    when 1..8 then 'sunny'
    when 9..10 then 'stormy'
  end
end

def stormy?
  set_weather weather_randomiser
  @weather == 'stormy'
end

It does introduce an awkward coupling between the randomiser and setter; changing the former requires looking at the latter ... ymmv

坏尐絯 2025-01-17 09:01:52

最简单的解决方案是这样的。不需要使用 any_instance_of,但也不建议在 subject 上存根任何内容。

describe Weather do
  subject(:weather) { described_class.new }

  before(:each) do
    allow(weather).to receive(:rand).with(1..10).and_return(random_value)
  end

  context 'when random value is in 1..8' do
    let(:random_value) { 8 }

    it { is_expected.not_to be_stormy }
  end

  context 'when random value is in 9..10' do
    let(:random_value) { 10 }

    it { is_expected.to be_stormy }
  end
end

另一种不暴露私有实现细节的编写方法如下:

describe Weather do
  subject(:weather) { described_class.new }

  let(:stormy_weathers) do
    (1..10).map do |val|
      allow(weather).to receive(:rand).and_return(val)
      weather.stormy?
    end
  end

  it 'is stormy 20% of the time and sunny 80% of the time' do
    expect(stormy_weathers)
      .to match_array(Array.new(2, true) + Array.new(8, false))
  end
end

即使您将内部实现更改为分别使用 1..2 和 3..10 作为晴天和暴风雨范围,此版本也会通过。只要比例仍然是80/20。

Simplest solution would be something like this. Doesn't require use of any_instance_of, although stubbing anything on subject is also not recommended.

describe Weather do
  subject(:weather) { described_class.new }

  before(:each) do
    allow(weather).to receive(:rand).with(1..10).and_return(random_value)
  end

  context 'when random value is in 1..8' do
    let(:random_value) { 8 }

    it { is_expected.not_to be_stormy }
  end

  context 'when random value is in 9..10' do
    let(:random_value) { 10 }

    it { is_expected.to be_stormy }
  end
end

Another way to write it without exposing private implementation details would be like this:

describe Weather do
  subject(:weather) { described_class.new }

  let(:stormy_weathers) do
    (1..10).map do |val|
      allow(weather).to receive(:rand).and_return(val)
      weather.stormy?
    end
  end

  it 'is stormy 20% of the time and sunny 80% of the time' do
    expect(stormy_weathers)
      .to match_array(Array.new(2, true) + Array.new(8, false))
  end
end

This version will pass even if you change the internal implementation to use 1..2 and 3..10 as the sunny and stormy ranges respectively. As long as the ratio is still 80/20.

仲春光 2025-01-17 09:01:52

我相信您可以使用以下代码片段。在这里,您想要对 rand 函数本身进行存根。我真的不建议这样做。如果您的类可能被称为“Randomizer”并且它包含“random”类,我建议您这样做。像 Object 这样的存根类根本就不好。

#!/usr/bin/env ruby
require_relative '../weather.rb'

RSpec.describe Weather do
  it 'randomises weather return stormy' do
    allow_any_instance_of(Object).to receive(:rand).and_return(9)
    weather = Weather.new
    expect(weather).to be_stormy
  end

  it 'randomises weather return sunny' do
    allow_any_instance_of(Object).to receive(:rand).and_return(4)
    weather = Weather.new
    expect(weather).to_not be_stormy
  end
end

I believe you can use the following snippet. Here you want to stub the rand function itself. I really don't recommend that. I would recommend if you have your class that may be called Randomizer and it contains the random class. Stubbing classes like Object is not good at all.

#!/usr/bin/env ruby
require_relative '../weather.rb'

RSpec.describe Weather do
  it 'randomises weather return stormy' do
    allow_any_instance_of(Object).to receive(:rand).and_return(9)
    weather = Weather.new
    expect(weather).to be_stormy
  end

  it 'randomises weather return sunny' do
    allow_any_instance_of(Object).to receive(:rand).and_return(4)
    weather = Weather.new
    expect(weather).to_not be_stormy
  end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文