-
-
Notifications
You must be signed in to change notification settings - Fork 461
Support mocking of Rng:: gen_range #1020
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
Comments
Is there a reason why you can't use PRNG seeded with a constant value, e.g. PCG? I guess a "straightforward" fix be to use |
@newpavlov Thanks for the response! I was not aware of alternative solutions. Is the suggestion that instead of mocking, I use a fixed seed in the unit test, so while I don't control what will be returned I will get the same values on the same run? |
Yes. Note that you do control return values to a certain extent, i.e. you can play with the constant seed to get a better sequence of test values (e.g. which provide full coverage faster). |
Indeed, the design of |
Wrapping Rng in my own class was another approach I considered, but I was hoping that the crate would provide that natively. I am a little apprehensive of having to create my own wrapper class to allow faking/mocking (reminds of wrapping C++ chrono clocks so I could fake them in tests). Naively (I know nothing): couldn't Rng become a trait with default implementation, so I could fully re-implement Rng with faked behavior? |
Originally Overriding |
We can discuss further if you like, but I believe we will not change this to support mocking. |
Spending some time working with this and using fixed seed, I don't think that will be sufficient. If I need to write a test that addresses a bug that happens in a very specific circumstances of random execution, I can't be guessing a seed to try to reproduce. The random process takes multiple steps[1], each with a random component. If the bug manifests only in a certain random events, I need to be able to set exactly that. As a result my only option now will be to create a wrapper around Rng to be able to fake it and feed it concrete results. This seems unnecessary and will likely now prevent me from using things like impl Distribution for Standard { let size: SettlementSize = rng.gen(); Because Instead I will be calling: DiceRoller::roll(1, 3) instead, which will not be related to Distribution, Standard or other class defined in random crates. [1] If you are familiar with tabletop role playing games I am making random choices on several steps of random tables to generate content. |
If you are doing things like Another option in your case would be to create a sampling helper like this: trait GameSampler {
fn settlement_size(&mut self) -> i32;
}
impl<R: Rng + ?Sized> GameSampler for R {
fn settlement_size(&mut self) -> i32 {
self.gen_range(1..=3);
}
}
struct MockSampler(SomeRng);
impl GameSampler for MockSampler {
fn settlement_size(&mut self) -> i32 {
2
}
} |
For what it's worth I was quite surprised when I discovered that Basically, I'd expect the following to print use rand::rngs::mock::StepRng;
use rand::Rng;
fn main() {
let mut rng = StepRng::new(0, 1);
let mut sample = vec![];
for _ in 0..8 {
sample.push(rng.gen_range(0, 7));
}
println!("{:?}", sample);
} The failure of the current behavior is surprising at beset. |
We've had quite a few people say this — lots of people expect |
With my understanding of the way the traits interact, I don't believe it would be possible to override the behavior for this struct specifically, at least until specialization lands on stable, correct? For my testing purposes (presumably others' as well), bias is fine. That's kind of the point of mocking, after all! Admittedly, I'm not sure how the behavior could change, but I (obviously) wish it were different. |
You're correct: it is not currently possible to override the behaviour, but might be with specialisation. Is it possible with min_specialization? If so we could implement a nightly-only feature for this. |
Background
What is your motivation?
I want to test code that uses rand module. Ideally, I would seed the mock with the random entries I want to produce, so I can confirm the end result is working as expected.
What type of application is this? (E.g. cryptography, game, numerical simulation)
game
Feature request
I am writing a small program that randomly selects an entry from an enum. Sample code:
Now, of course I want to test it. That's why get_settlement_size takes the rng value.
Unfortunately, this doesn't work. When I added some printlns, the value returned from:
is always 0. I copied StepRng code into my test module to add println inside and I see next_u32 and next_u64 called. However, later the code disappears into UniformSampler and at that point it becomes too hard for me to follow.
Ideally, I would mock
gen_range
directly.The text was updated successfully, but these errors were encountered: