Skip to content

Commit

Permalink
Make RandomExample take an optional 'seed' parameter
Browse files Browse the repository at this point in the history
The previous approach to deterministic behaviour was to rely on
'srand' being set upstream, so that it is up to the app to define
a seed if they wish to have consistent results.

However, if the srand is not set explicitly by the developer,
and if the test framework (such as RSpec) does not set the seed
to a random value, then the 'random example' will be the same
example every time, by default. This could be surprising.

We decided it is better to make the developer explicitly opt in
to seed behaviour, than it is to make them set a random seed in
order to get a 'true' random on every test run.

This commit is part 1: defining the 'seed' parameter. Part 2
comes in the next commit, where we'll swap out the call to 'srand'
and use an instance of Random, to prevent changing the global
state of things.

Full discussion:
https://github.com/alphagov/govuk_schemas/pull/56/files\#r458182188
  • Loading branch information
ChrisBAshton committed Jul 23, 2020
1 parent 241b921 commit 9ddca1d
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 12 deletions.
14 changes: 7 additions & 7 deletions lib/govuk_schemas/random_example.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
require "govuk_schemas/random"
require "govuk_schemas/random_item_generator"
require "json-schema"
require "json"
Expand All @@ -24,16 +23,17 @@ class RandomExample
# schema = GovukSchemas::Schema.find(frontend_schema: "detailed_guide")
# GovukSchemas::RandomExample.new(schema: schema).payload
#
# If you need a 'consistent' random response, you can set the seed using
# (for example) `srand(777)` and know that the payload will always be the
# same. Note that timestamps in the payload will continue to reflect the
# current time.
# Example with seed (for consistent results):
#
# schema = GovukSchemas::Schema.find(frontend_schema: "detailed_guide")
# GovukSchemas::RandomExample.new(schema: schema, seed: 777).payload
# GovukSchemas::RandomExample.new(schema: schema, seed: 777).payload # returns same as above
#
# @param [Hash] schema A JSON schema.
# @return [GovukSchemas::RandomExample]
def initialize(schema:)
def initialize(schema:, seed: nil)
@schema = schema
@random_generator = RandomItemGenerator.new(schema: schema)
@random_generator = RandomItemGenerator.new(schema: schema, seed: seed)
end

# Returns a new `GovukSchemas::RandomExample` object.
Expand Down
3 changes: 2 additions & 1 deletion lib/govuk_schemas/random_item_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ module GovukSchemas
#
# @private
class RandomItemGenerator
def initialize(schema:)
def initialize(schema:, seed: nil)
@schema = schema
srand(seed) unless seed.nil?
end

def payload
Expand Down
6 changes: 2 additions & 4 deletions spec/lib/random_example_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@
schema = GovukSchemas::Schema.random_schema(schema_type: "frontend")
# freeze time to avoid inconsistent `public_updated_at` values between runs
Timecop.freeze do
srand(777) # these srand calls would be in the upstream application
first_payload = GovukSchemas::RandomExample.new(schema: schema).payload
srand(777)
second_payload = GovukSchemas::RandomExample.new(schema: schema).payload
first_payload = GovukSchemas::RandomExample.new(schema: schema, seed: 777).payload
second_payload = GovukSchemas::RandomExample.new(schema: schema, seed: 777).payload
expect(first_payload).to eql(second_payload)
end
end
Expand Down

0 comments on commit 9ddca1d

Please sign in to comment.