Open flakey-bit opened 3 years ago
Hey @flakey-bit
Under the hood, the auto generate handlers create a new instances of the Bogus Faker
, which is why the seed is being lost. I will look at hooking the UseSeed
on the AutoFaker
up so it propagates to the underling faker instance. In the meantime, a UseFakerHub
configuration handler was added a few months back that allows you to create and provide your own faker instance. This will allow the seed you set to be honoured.
Nick.
Hey @flakey-bit
I had a further look into this and all seeding is proxied through to the underlying Bogus.Faker instance. If a seed is not used then all the dates generated are random. However with a seed each faker instance generates a date based on the DateTime.Now, so there are some slight variations (milliseconds) but still based on the provided seed.
I couldn't see any changes that were needed for AutoBogus so may be it is something to raise with the Bogus project.
Nick.
Hi @nickdodd79,
Thanks for getting back to me. I've tested it using Bogus.Faker
directly and it works just fine I have been able to reproduce the problem:
public async Task DateTimeOffsetShouldBeIdenticalWhenSeedUsed()
{
int seed = 1;
var faker1 = new Faker<MyEntity>().UseSeed(seed);
faker1.RuleFor(e => e.DeprecationDate, (f, e) => f.Date.Future());
var entity1 = faker1.Generate();
await Task.Delay(TimeSpan.FromSeconds(5));
var faker2 = new Faker<MyEntity>().UseSeed(seed);
faker2.RuleFor(e => e.DeprecationDate, (f, e) => f.Date.Future());
var entity2 = faker2.Generate();
Assert.Equal(entity2.Name, entity1.Name); // Passes
Console.Out.WriteLine(entity2.DeprecationDate.ToUnixTimeMilliseconds());
Console.Out.WriteLine(entity2.DeprecationDate);
Assert.Equal(entity2.DeprecationDate, entity1.DeprecationDate); // Fails
}
See these notes from Bogus:
Bogus can generate deterministic dates and times. However, generating deterministic dates and times requires the following:
Anyway, I found that adding the line
Bogus.DataSets.Date.SystemClock = () => DateTime.Parse("8/8/2019 2:00 PM");
to the test makes it pass. I guess my question is, should this API be wrapped (and exposed) by AutoBogus?
Hi @flakey-bit
Thanks for the update. I can see that switching AutoFaker
for Faker
does make the tests pass, which is certainly interesting. It is also good to see that you have managed to replicate the issue with Faker
instances.
In terms of setting up the anchor time, one consideration is that it is a static, so we can't set that on a 'per generate' basis. I can certainly add a WithSystemClock
config handler to AutoFaker.Configure
. It would then basically wrap the above SystemClock setting. Would that help with your use case?
Nick.
Thanks for the update. I can see that switching AutoFaker for Faker does make the tests pass, which is certainly interesting
That's because (without the rule), Bogus.Faker
will leave the values as DateTime.MinValue
- which was why I updated the second test case to include the line (for each faker)
faker1.RuleFor(e => e.DeprecationDate, (f, e) => f.Date.Future());
I wonder if it's worth bringing this issue up w/ the maintainer of Bogus.Faker and seeing if they'd consider adding something to help deal with it better. Feels like it should be settable on the faker instance itself (falling back to the static version otherwise).
Will leave it with you to do what you feel is appropriate. Feel free to close this issue - thanks for taking the time to investigate.
Thanks @flakey-bit.
@bchavez do you have any input into this. Could we add SystemClock configuration at a FakerHub level so we can isolate and control baseline date generation?
Hey @nickdodd79,
Sorry for the late reply! Been super busy with the day job stuff.
I'm not totally sure yet; I think it might take some re-work. I think it comes down to:
Currently, we can anchor the date/time generations in two ways:
Bogus.DataSets.Date.SystemClock
- which is applies to all Faker<T>
, Faker
, and new Date()
instances.refDate
parameter.By convention, if refDate
is specified, SystemClock
is ignored.
I think there's a special case where new Person()
only uses the SystemClock
.
In summary, currently, the order of precedence for getting an anchor in time is,
refDate
-> Static Date.SystemClock
Essentially, (I think) what we're proposing is changing the precedence order to:
refDate
-> DataSet InstanceClock
-> Static Date.SystemClock
Similar to what we do now with the Random
seed.Overall, I would say this is probably a medium-lift depending on how it goes. Seed propagation throughout Faker<T>
-> FakerHub
-> DataSets is kind of a complex process to get right.
Backwards compatibility might be okay.
Where possible, I'd probably also take the opportunity to re-work .UseSeed(int val)
to include an optional parameter to anchor time too. ie: .UseSeed(int val, DateTime? anchor = null)
because semantically, when you set any seed, you'd probably also most likely want to set an anchor in time for deterministic date/time behavior too. This would at least surface the "time anchor" issue closer to where the developer would have expected it.
Let me know your thoughts.
Thanks, Brian
Where possible, I'd probably also take the opportunity to re-work
.UseSeed(int val)
to include an optional parameter to anchor time too. ie:.UseSeed(int val, DateTime? anchor = null)
because semantically, when you set any seed, you'd probably also most likely want to set an anchor in time for deterministic date/time behavior too. This would at least surface the "time anchor" issue closer to where the developer would have expected it.
💯
Population of
DateTimeOffset
properties does not respect the seed.Failing test case: