Skip to content
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

feat: add class for mocking Math.random #233

Merged
merged 2 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/curriculum-helpers.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Curriculum Helpers

## RandomMocker

Mocks Math.random for testing purposes. Each time `mock()` is called the pseudo-random number generator is reset to its initial state, so that the same sequence of random numbers is generated each time. `restore()` restores the native Math.random function.

```javascript
const randomMocker = new RandomMocker();
randomMocker.mock();
Math.random(); // first call is always 0.2523451747838408
Math.random(); // second call is always 0.08812504541128874
randomMocker.mock();
Math.random(); // generator is reset, so we get 0.2523451747838408 again
randomMocker.restore();
Math.random(); // back to native Math.random
```

## concatRegex

Combines one or more regular expressions into one.
Expand Down
45 changes: 45 additions & 0 deletions lib/__tests__/curriculum-helper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,51 @@ const {
jsCodeWithCommentedCall,
} = jsTestValues;

describe("RandomMocker", () => {
let random: () => number;

beforeEach(() => {
random = Math.random;
});

afterEach(() => {
Math.random = random;
});

describe("mock", () => {
it('should replace "Math.random" with a mock function', () => {
const mocker = new helper.RandomMocker();
mocker.mock();
expect(Math.random).not.toBe(random);
});

it('should mock "Math.random" with a pseudorandom function', () => {
const mocker = new helper.RandomMocker();
mocker.mock();
// Predictable random values:
expect(Math.random()).toBe(0.2523451747838408);
expect(Math.random()).toBe(0.08812504541128874);
});

it("should reset the pseudorandom function when called multiple times", () => {
const mocker = new helper.RandomMocker();
mocker.mock();
expect(Math.random()).toBe(0.2523451747838408);
mocker.mock();
expect(Math.random()).toBe(0.2523451747838408);
});
});

describe("restore", () => {
it('should restore "Math.random" to its original function', () => {
const mocker = new helper.RandomMocker();
mocker.mock();
mocker.restore();
expect(Math.random).toBe(random);
});
});
});

describe("removeWhiteSpace", () => {
const { removeWhiteSpace } = helper;
it("returns a string", () => {
Expand Down
31 changes: 31 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,37 @@
import { strip } from "./strip";
import astHelpers from "../python/py_helpers.py";

/**
* The `RandomMocker` class provides functionality to mock and restore the global `Math.random` function.
* It replaces the default random number generator with a deterministic pseudo-random number generator.
*/
export class RandomMocker {
private random: () => number;

constructor() {
this.random = Math.random;
}

private createRandom() {
let seed = 42;
const a = 1664525;
const c = 1013904223;
const mod = 2 ** 32;
return () => {
seed = (a * seed + c) % mod;
return seed / mod;
};
}

mock(): void {
globalThis.Math.random = this.createRandom();
}

restore(): void {
globalThis.Math.random = this.random;
}
}

/**
* Removes every HTML-comment from the string that is provided
* @param {String} str a HTML-string where the comments need to be removed of
Expand Down
Loading