-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Improve #deterministically_verify
helper
#2828
Changes from all commits
2d69e18
b218d94
3c21e19
a06f8f3
afa915e
48771fd
68bf990
bebd0ac
f91d210
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,21 +23,20 @@ | |
# times with the same deterministic_random seed. | ||
# @param subject_proc [Proc] a proc object that returns the subject under test | ||
# when called. | ||
# @param depth [Integer] the depth of deterministic comparisons to run. | ||
# @param random [Integer] A random number seed; Used to override the default. | ||
# @param depth [Integer] the depth of deterministic comparisons to run; the default value is 2. | ||
# @param seed [Integer] A random number seed; Used to override the default value which is 42. | ||
# | ||
# @example | ||
# deterministically_verify ->{ @tester.username('bo peep') } do |subject| | ||
# assert subject.match(/(bo(_|\.)peep|peep(_|\.)bo)/) | ||
# end | ||
# | ||
def deterministically_verify(subject_proc, depth: 2, random: nil, &block) | ||
raise 'need block' unless block_given? | ||
def deterministically_verify(subject_proc, depth: 2, seed: 42) | ||
results = depth.times.map do | ||
Faker::Config.stub :random, Random.new(seed) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding is that you want to stub random here to guarantee that every iteration will use the same random instance. I think the desired outcome is that generators should never override/change the random generator during their execution, and we want to have an easy way to assert that with In my view, if we stub random here, I think we might not be able to catch generators that try to set a new value for random again during their execution, so Maybe a different approach could be to set the random instance like you're doing it here. And them maybe stub the setter Or, maybe an even simpler approach would be to check that the seed is the same before and after the iteration to make sure the generator never changes it. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, that's a great point! +1 to check the seed is the same before and after the iteration. Abut checking this on multiple threads: @erichmachado are you up for working on that as well? There are these tests that might be helpful: https://github.com/faker-ruby/faker/blob/main/test/test_default_locale.rb and https://github.com/faker-ruby/faker/blob/main/test/faker/default/test_faker_unique_generator.rb Otherwise, we can create a new issue for that. Thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @thdaraujo, thank you for your time and your review. Much appreciated! I'll try to explain the rationale behind it below. Please, let me know if you still have questions.
Guaranteeing that every internal The motivation to iterate on The problem was that before we were using a memoized Faker::Config.random = random || Random.new(42) Which we could have fixed with an update like this: Faker::Config.random = Random.new(seed) || Random.new(42) And if we assign Faker::Config.random = Random.new(seed) But here lies another issue: without stubbing, the Stubbing guarantees that every internal For example, it's possible to verify that the
Ideally, just set the I assume we don't want it to mutate just because the helper was called, even though we expect the code exercised inside the block to have access to the alternate RNG while running.
Based on the analysis above, I believe this change will add to the protection in this direction, but not for the generators (library) code but rather for the specs themselves. If we want to ensure that library code is not mutating global state, then I recommend relying on a partial double assertion on In summary, both approaches would complement each other, from my understanding. One is protecting the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh yeah, now I get it. Thanks for the in-depth explanation! I haven't considered this part when I was reading your proposal:
And if you're interested in working on the guard against generators changing the |
||
yield subject_proc.call.freeze | ||
end | ||
end | ||
|
||
# rubocop:disable Style/MultilineBlockChain | ||
depth.times.inject([]) do |results, _index| | ||
Faker::Config.random = random || Random.new(42) | ||
results << subject_proc.call.freeze.tap(&block) | ||
end.repeated_combination(2) { |(first, second)| assert_equal first, second } | ||
# rubocop:enable Style/MultilineBlockChain | ||
results.combination(2) { |(first, second)| assert_equal first, second } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice catch! 😎 |
||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I like that name. It says more about it is.