-
-
Notifications
You must be signed in to change notification settings - Fork 765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Randomization is difficult to reproduce #1094
Comments
I haven't had a problem with this, and in my experience |
This repo contains a sample that consistently fails the included Cucumber feature for me. The feature records the output from the first RSpec run and checks it against a second run. https://bitbucket.org/ToadJamb/ruby-rspec-randomization-example Assuming the Cucumber features fail for others, there is a third option that I like better than either of the two I mentioned above. It would be to implement the first option above, but provide a seed regardless so that randomization can be reproduced for a given test run (regardless of whether the specs are run in a random order). This is a potential problem regardless of the ordering strategy. |
@myronmarston he's talking about randomisation in his code, not in our code. E.g. We are not locking down the randomisation across the board, which would seem legit. |
@ToadJamb -- I'm not a mercurial user (I've never even installed it) and don't have any desire to spend the time learning now. I tried git-remote-hg to allow me to clone your repo using git, but it gave me an error. Can you push that to a git repository so I can clone it? Thanks! |
Sure. Try this: https://github.com/ToadJamb/ruby-rspec-randomization-example |
@myronmarston FYI I downloaded the repo (via a zip from bitbucket) and it does randomise differently, but this isn't internal RSpec code, is our seed supposed to leak out into a developers implementation? |
You're right. This isn't internal RSpec code. But there are two approaches RSpec can take to randomization.
Right now, RSpec is attempting to take a hands-off approach, but isn't. The call to Ideally, I would like to see RSpec pursue the first option, as I think it is the most useful to developers. I didn't realize that I needed to handle seeding randomization myself until my failures were not reproduced when passing in the seed from the failed test run. I think most people would fall into this category and it's rather frustrating to find out at a point that is too late to help. |
The support for randomisation that exists at the moment is for ordering our specs in a reproducible random order only. AFAIK the code is not attempting to support your own randomisation. Note that our call to |
"Note that our call to srand occurs before your code is executed." In the case that I have rspec order my tests randomly, this is not true at all. Adding It would be trivial (and seems quite obviously better) to do this: def order(items)
@used = true
strategy = Random.new(@configuration.seed)
items.shuffle(random: strategy)
end instead of def order(items)
@used = true
Kernel.srand @configuration.seed
ordering = items.shuffle
Kernel.srand # reset random generation
ordering
end The most obvious thing here is that you are making a global call to srand that is absolutely not necessary and is having side effects that are clearly not intended. Is there a reason not to fix this? Is there a problem with the code I'm suggesting? For the record, I'm more than willing to write the tests and submit a patch. |
Ruby 1.8.7 doesn't support your suggestion. |
However I'm still not sure what the problem is. We aren't preventing you from reseeding for your own needs, we are not trying to make your randomisation predictable, only our suite ordering. |
If you want predictable ordering of course you have to reset the seed to a known value before each test, how else would it work. |
I wasn't sure if you were still supporting 1.8.7. If so, then that is certainly a fair argument against my suggestion. At the least, I would like to be able to set it in a |
As a testing tool we kind of have to, we can't force people to upgrade and there are a lot of gems which support 1.8.7 with test suites written in RSpec.
We are not trying to enable predictable randomisation for your code. If you want two examples to display the same psuedorandom pattern then the seed must be reset before each example, there is no other alternative. I am heavily against that, it's an overhead most users won't need, you can easily do it yourself. If you want two examples to display different randomisation then you can leave it as is. |
While I don't entirely agree, I certainly respect your thoughts. My final argument for RSpec seeding randomization goes something like this... NOT setting a seed for randomization violates the principle of least surprise. Most developers won't care about how randomization is seeded until there is a test failure/error due to randomization. At that point, it will be too late for them to reproduce the failure/error. This is exactly what happened to me - except that I do care and made the incorrect assumption that RSpec was handling this for me. |
@ToadJamb -- First off, thanks for the very clear code example that shows the issue! @JonRowe is right that RSpec's I agree that RSpec's current way of managing the seed actively gets in the way of users because it's basically impossible for users to set the random seed once and have it "stick" since RSpec resets it. So yes, at a minimum we should fix that, and more than that, I think I'd like to see @ToadJamb -- want to take a stab at fixing this? |
@myronmarston I'll be happy to take a shot at this. |
I prefer to know that I'm not breaking existing/expected functionality before I go hacking around in code. To that end, I was looking for existing Cucumber features around ordering and seeding but didn't find any, so I started writing some. What is the best way to discuss this code? I'm not opposed to submitting a pull request, but I'd just like to have a discussion around what I'm doing to make sure it's ok. I'm not looking to have it pulled in just yet. Ideally, I would like to be able to do this as I go along, so I don't spend time going too far down paths that don't make sense. |
There are ones surrounding ordering. |
Sweet. I mistakenly assumed those specs would be in Cucumber. Is the behavior of It looks like doing that currently throws the ordering into random, but I think it makes sense (with this change) that it would leave the specs unordered and only use the given seed for seeding randomization. |
I prefer to take things in small, iterative steps. Here is my outline for this issue (each item will ultimately be a pull request):
|
FWIW, prior to now, However, I think with the direction we're talking about taking this, decoupling them makes sense. |
This sounds fine, but I'd rather just get one PR for the entire thing. IMO, this change as as whole makes sense, but doing this first step w/o the rest doesn't make much sense: as long as long as the seed is only usable for RSpec's random orderer, having
👍 That also takes care of the complexity we've discussed here :).
Huh? The seed is already randomized. I don't follow...
Not sure I follow this, either. Why is doing it in that order important? |
On a side note, if you want to use the |
Agreed. My primary goal with that was to get feedback as early as possible, but you're right, merging those in individually doesn't really make sense.
Awesome! I was attempting to find a solution that would not require new dependencies, but bringing in backports would make things a lot easier. So now, I'm thinking of the work like this:
|
I don't want to add backports as a dependency for just the random stuff. Instead, we'd probably port its code over to rspec, like so: module RSpec
module Core
if defined?(::Random)
Random = ::Random
else
require 'rspec/core/random' # which defines RSpec::Core::Random based on backports version
end
end
end The reason for this is that besides the added dependency, we don't want RSpec to add a 1.9 stdlib class to the top level namespace. Reason being, a user testing a gem on 1.8.7 and 1.9 could use |
That makes sense. I just hadn't taken the time to process the implications. It may be worth noting that |
Closing since this was addressed by #1120. |
The implementation of randomizing tests is such that locking down other randomizations is difficult (i.e. shuffling, calling rand, etc. do not yield the same results for a given seed).
The only way I've found to do it is to add something like:
This is not ideal because now the randomization is reset before each and every test, making the randomizations over the course of an entire suite much less... 'random'.
There are two potential solutions:
Do not use srand for randomization. Do something like:
Use srand, but let RSpec's seed be used for everything after that. Like so:
The first option still requires the consumer to write code to call
srand RSpec.configuration.seed
, but it can be done once in a support test file orbefore(:suite)
, orbefore(:all)
.I prefer the second option, as it means that I do nothing as an RSpec consumer in order to get repeatable randomization (I swear RSpec worked this way once upon a time). Keep in mind that this can be 'turned off' the same way it is 'turned on', so it simply requires calling
srand
with no arguments.I am working on a patch to fix this, but would like feedback on the preferred approach.
If the second choice is preferrable, it is probably worth taking the time to ensure that it only gets set once. It appears that the ordering code gets called multiple times - based on sample test suites that I used to play with this. Ideally,
srand
would only be called once with the seed for that run.The text was updated successfully, but these errors were encountered: