I have a method that does something like this:
我有一个类似这样的方法:
def some_method
chance = rand(4)
if chance == 1 do
# logic here
else
# another logic here
end
end
When I use RSpec to test this method, rand(4)
inside it always generates 0. I am not testing rand
method of Rails, I am testing my method. What is a common practice to test my method?
当我使用RSpec来测试这个方法时,它内部的rand(4)总是生成0.我没有测试Rails的rand方法,我正在测试我的方法。测试我方法的常见做法是什么?
4 个解决方案
#1
32
There are two approaches I would consider:
我会考虑两种方法:
Approach 1:
方法1:
Use a known value of seed in srand( seed )
in a before :each
block:
在before:each块中的srand(seed)中使用已知的种子值:
before :each do
srand(67809)
end
This works across Ruby versions, and gives you control in case you want to cover particular combinations. I use this approach a lot - thinking about it, that's because the code I was testing uses rand()
primarily as a data source, and only secondarily (if at all) for branching. Also it gets called a lot, so exerting call-by-call control over returned values would be counter-productive, I would end up shovelling in lots of test data that "looked random", probably generating it in the first place by calling rand()
!
这适用于Ruby版本,并在您想要涵盖特定组合的情况下为您提供控制。我经常使用这种方法 - 考虑一下,这是因为我测试的代码主要使用rand()作为数据源,而仅使用其次(如果有的话)进行分支。它也被调用很多,所以对返回值进行逐个调用控制会适得其反,我最终会铲掉许多“看起来随机”的测试数据,可能首先通过调用rand来生成它()!
You may wish to call your method multiple times in at least one test scenario to ensure you have reasonable coverage of combinations.
您可能希望在至少一个测试场景中多次调用您的方法,以确保您有合理的组合覆盖率。
Approach 2:
方法2:
If you have branch points due to values output from rand()
and your assertions are of the type "if it chooses X, then Y should happen", then it is also reasonable in the same test suite to mock out rand( n )
with something that returns the values you want to make assertions about:
如果由于rand()输出的值而导致分支点,并且你的断言属于“如果它选择X,那么Y应该发生”,那么在同一个测试套件中模拟rand(n)也是合理的返回你想要断言的值的东西:
require 'mocha/setup'
Kernel.expects(:rand).with(4).returns(1)
# Now run your test of specific branch
In essence these are both "white box" test approaches, they both require you to know that your routine uses rand()
internally.
从本质上讲,这些都是“白盒子”测试方法,它们都要求您知道您的例程在内部使用rand()。
A "black box" test is much harder - you would need to assert that behaviour is statistically OK, and you would also need to accept a very wide range of possibilities since valid random behaviour could cause phantom test failures.
“黑匣子”测试要困难得多 - 您需要断言行为在统计上是正常的,并且您还需要接受非常广泛的可能性,因为有效的随机行为可能会导致幻像测试失败。
#2
13
I'd extract the random number generation:
我将提取随机数生成:
def chance
rand(4)
end
def some_method
if chance == 1 do
# logic here
else
# another logic here
end
end
And stub it:
它存根:
your_instance.stub(:chance) { 1 }
This doesn't tie your test to the implementation details of rand
and if you ever decide to use another random number generator, your test doesn't break.
这不会将您的测试与rand的实现细节联系起来,如果您决定使用另一个随机数生成器,那么您的测试不会中断。
#3
12
It seems that best idea is to use stub, instead of real rand
. This way you would be able to test all values that you are interested in. As rand
is defined in Kernel
module you should stub it using:
似乎最好的想法是使用存根,而不是真正的兰德。这样你就可以测试你感兴趣的所有值。由于rand是在内核模块中定义的,你应该使用它来存根:
Kernel.stub(:rand).with(anything) { randomized_value }
In particular contexts you can define randomized_value
with let
method.
在特定的上下文中,您可以使用let方法定义randomized_value。
#4
6
I found that just stubbing rand ie. using Kernel.stub(:rand) as answered by Samuil did not initially work. My code to be tested called rand directly e.g
我发现只是短暂的兰德即。使用Kernel.stub(:rand)作为Samuil的回答并没有最初的工作。我要测试的代码直接称为rand,例如
random_number = rand
random_number = rand
However, if I changed the code to
但是,如果我将代码更改为
random_number = Kernel.rand
random_number = Kernel.rand
then the stubbing worked.
然后茬工作了。
#1
32
There are two approaches I would consider:
我会考虑两种方法:
Approach 1:
方法1:
Use a known value of seed in srand( seed )
in a before :each
block:
在before:each块中的srand(seed)中使用已知的种子值:
before :each do
srand(67809)
end
This works across Ruby versions, and gives you control in case you want to cover particular combinations. I use this approach a lot - thinking about it, that's because the code I was testing uses rand()
primarily as a data source, and only secondarily (if at all) for branching. Also it gets called a lot, so exerting call-by-call control over returned values would be counter-productive, I would end up shovelling in lots of test data that "looked random", probably generating it in the first place by calling rand()
!
这适用于Ruby版本,并在您想要涵盖特定组合的情况下为您提供控制。我经常使用这种方法 - 考虑一下,这是因为我测试的代码主要使用rand()作为数据源,而仅使用其次(如果有的话)进行分支。它也被调用很多,所以对返回值进行逐个调用控制会适得其反,我最终会铲掉许多“看起来随机”的测试数据,可能首先通过调用rand来生成它()!
You may wish to call your method multiple times in at least one test scenario to ensure you have reasonable coverage of combinations.
您可能希望在至少一个测试场景中多次调用您的方法,以确保您有合理的组合覆盖率。
Approach 2:
方法2:
If you have branch points due to values output from rand()
and your assertions are of the type "if it chooses X, then Y should happen", then it is also reasonable in the same test suite to mock out rand( n )
with something that returns the values you want to make assertions about:
如果由于rand()输出的值而导致分支点,并且你的断言属于“如果它选择X,那么Y应该发生”,那么在同一个测试套件中模拟rand(n)也是合理的返回你想要断言的值的东西:
require 'mocha/setup'
Kernel.expects(:rand).with(4).returns(1)
# Now run your test of specific branch
In essence these are both "white box" test approaches, they both require you to know that your routine uses rand()
internally.
从本质上讲,这些都是“白盒子”测试方法,它们都要求您知道您的例程在内部使用rand()。
A "black box" test is much harder - you would need to assert that behaviour is statistically OK, and you would also need to accept a very wide range of possibilities since valid random behaviour could cause phantom test failures.
“黑匣子”测试要困难得多 - 您需要断言行为在统计上是正常的,并且您还需要接受非常广泛的可能性,因为有效的随机行为可能会导致幻像测试失败。
#2
13
I'd extract the random number generation:
我将提取随机数生成:
def chance
rand(4)
end
def some_method
if chance == 1 do
# logic here
else
# another logic here
end
end
And stub it:
它存根:
your_instance.stub(:chance) { 1 }
This doesn't tie your test to the implementation details of rand
and if you ever decide to use another random number generator, your test doesn't break.
这不会将您的测试与rand的实现细节联系起来,如果您决定使用另一个随机数生成器,那么您的测试不会中断。
#3
12
It seems that best idea is to use stub, instead of real rand
. This way you would be able to test all values that you are interested in. As rand
is defined in Kernel
module you should stub it using:
似乎最好的想法是使用存根,而不是真正的兰德。这样你就可以测试你感兴趣的所有值。由于rand是在内核模块中定义的,你应该使用它来存根:
Kernel.stub(:rand).with(anything) { randomized_value }
In particular contexts you can define randomized_value
with let
method.
在特定的上下文中,您可以使用let方法定义randomized_value。
#4
6
I found that just stubbing rand ie. using Kernel.stub(:rand) as answered by Samuil did not initially work. My code to be tested called rand directly e.g
我发现只是短暂的兰德即。使用Kernel.stub(:rand)作为Samuil的回答并没有最初的工作。我要测试的代码直接称为rand,例如
random_number = rand
random_number = rand
However, if I changed the code to
但是,如果我将代码更改为
random_number = Kernel.rand
random_number = Kernel.rand
then the stubbing worked.
然后茬工作了。