aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorST-DDT <[email protected]>2024-11-12 09:19:28 +0100
committerGitHub <[email protected]>2024-11-12 08:19:28 +0000
commit1633c8deb8f6f41151b39b62689ae3e02ab295bc (patch)
tree2561a719b9154d67c51747a2263f68baf4ff8efa
parentcb4b77c04f4d4a706819a6f34ff8378afec6523e (diff)
downloadfaker-1633c8deb8f6f41151b39b62689ae3e02ab295bc.tar.xz
faker-1633c8deb8f6f41151b39b62689ae3e02ab295bc.zip
feat: add initial seed parameter to constructors (#3220)
-rw-r--r--src/faker.ts16
-rw-r--r--src/internal/seed.ts8
-rw-r--r--src/simple-faker.ts27
-rw-r--r--src/utils/mersenne.ts17
-rw-r--r--test/faker.spec.ts83
-rw-r--r--test/internal/seed.spec.ts11
-rw-r--r--test/simple-faker.spec.ts56
7 files changed, 187 insertions, 31 deletions
diff --git a/src/faker.ts b/src/faker.ts
index 77630d29..0b03788b 100644
--- a/src/faker.ts
+++ b/src/faker.ts
@@ -125,6 +125,10 @@ export class Faker extends SimpleFaker {
* Specify this only if you want to use it to achieve a specific goal,
* such as sharing the same random generator with other instances/tools.
* Defaults to faker's Mersenne Twister based pseudo random number generator.
+ * @param options.seed The initial seed to use.
+ * The seed can be used to generate reproducible values.
+ * Refer to the `seed()` method for more information.
+ * Defaults to a random seed.
*
* @example
* import { Faker, es } from '@faker-js/faker';
@@ -157,8 +161,18 @@ export class Faker extends SimpleFaker {
* @default generateMersenne53Randomizer()
*/
randomizer?: Randomizer;
+
+ /**
+ * The initial seed to use.
+ * The seed can be used to generate reproducible values.
+ *
+ * Refer to the `seed()` method for more information.
+ *
+ * Defaults to a random seed.
+ */
+ seed?: number;
}) {
- super({ randomizer: options.randomizer });
+ super({ randomizer: options.randomizer, seed: options.seed });
let { locale } = options;
diff --git a/src/internal/seed.ts b/src/internal/seed.ts
new file mode 100644
index 00000000..3ff484b2
--- /dev/null
+++ b/src/internal/seed.ts
@@ -0,0 +1,8 @@
+/**
+ * Generates a random seed.
+ *
+ * @internal
+ */
+export function randomSeed(): number {
+ return Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER);
+}
diff --git a/src/simple-faker.ts b/src/simple-faker.ts
index 743df923..1371e68a 100644
--- a/src/simple-faker.ts
+++ b/src/simple-faker.ts
@@ -1,3 +1,4 @@
+import { randomSeed } from './internal/seed';
import { DatatypeModule } from './modules/datatype';
import { SimpleDateModule } from './modules/date';
import { SimpleHelpersModule } from './modules/helpers';
@@ -97,6 +98,10 @@ export class SimpleFaker {
* Specify this only if you want to use it to achieve a specific goal,
* such as sharing the same random generator with other instances/tools.
* Defaults to faker's Mersenne Twister based pseudo random number generator.
+ * @param options.seed The initial seed to use.
+ * The seed can be used to generate reproducible values.
+ * Refer to the `seed()` method for more information.
+ * Defaults to a random seed.
*
* @example
* import { SimpleFaker } from '@faker-js/faker';
@@ -120,11 +125,25 @@ export class SimpleFaker {
* @default generateMersenne53Randomizer()
*/
randomizer?: Randomizer;
+
+ /**
+ * The initial seed to use.
+ * The seed can be used to generate reproducible values.
+ *
+ * Refer to the `seed()` method for more information.
+ *
+ * Defaults to a random seed.
+ */
+ seed?: number;
} = {}
) {
- const { randomizer = generateMersenne53Randomizer() } = options;
+ const { randomizer, seed } = options;
+
+ if (randomizer != null && seed != null) {
+ randomizer.seed(seed);
+ }
- this._randomizer = randomizer;
+ this._randomizer = randomizer ?? generateMersenne53Randomizer(seed);
}
/**
@@ -247,9 +266,7 @@ export class SimpleFaker {
* @since 6.0.0
*/
seed(seed?: number | number[]): number | number[];
- seed(
- seed: number | number[] = Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
- ): number | number[] {
+ seed(seed: number | number[] = randomSeed()): number | number[] {
this._randomizer.seed(seed);
return seed;
diff --git a/src/utils/mersenne.ts b/src/utils/mersenne.ts
index 315335ab..585f84b5 100644
--- a/src/utils/mersenne.ts
+++ b/src/utils/mersenne.ts
@@ -1,10 +1,13 @@
import { MersenneTwister19937 } from '../internal/mersenne';
+import { randomSeed } from '../internal/seed';
import type { Randomizer } from '../randomizer';
/**
* Generates a MersenneTwister19937 randomizer with 32 bits of precision.
* This is the default randomizer used by faker prior to v9.0.
*
+ * @param seed The initial seed to use. Defaults to a random number.
+ *
* @example
* import { de, en, generateMersenne32Randomizer, Faker } from '@faker-js/faker';
*
@@ -16,10 +19,12 @@ import type { Randomizer } from '../randomizer';
*
* @since 8.2.0
*/
-export function generateMersenne32Randomizer(): Randomizer {
+export function generateMersenne32Randomizer(
+ seed: number = randomSeed()
+): Randomizer {
const twister = new MersenneTwister19937();
- twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
+ twister.initGenrand(seed);
return {
next(): number {
@@ -39,6 +44,8 @@ export function generateMersenne32Randomizer(): Randomizer {
* Generates a MersenneTwister19937 randomizer with 53 bits of precision.
* This is the default randomizer used by faker starting with v9.0.
*
+ * @param seed The initial seed to use. Defaults to a random number.
+ *
* @example
* import { de, en, generateMersenne53Randomizer, Faker } from '@faker-js/faker';
*
@@ -50,10 +57,12 @@ export function generateMersenne32Randomizer(): Randomizer {
*
* @since 9.0.0
*/
-export function generateMersenne53Randomizer(): Randomizer {
+export function generateMersenne53Randomizer(
+ seed: number = randomSeed()
+): Randomizer {
const twister = new MersenneTwister19937();
- twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
+ twister.initGenrand(seed);
return {
next(): number {
diff --git a/test/faker.spec.ts b/test/faker.spec.ts
index 6d928d63..dba19421 100644
--- a/test/faker.spec.ts
+++ b/test/faker.spec.ts
@@ -1,18 +1,10 @@
import type { MockInstance } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
-import { Faker, faker } from '../src';
+import { Faker, faker, generateMersenne32Randomizer } from '../src';
import { FakerError } from '../src/errors/faker-error';
import { keys } from '../src/internal/keys';
describe('faker', () => {
- it('should throw error if no locales passed', () => {
- expect(() => new Faker({ locale: [] })).toThrow(
- new FakerError(
- 'The locale option must contain at least one locale definition.'
- )
- );
- });
-
it('should not log anything on startup', async () => {
const spies: MockInstance[] = keys(console)
.filter((key) => typeof console[key] === 'function')
@@ -69,19 +61,70 @@ describe('faker', () => {
});
});
- describe('randomizer', () => {
- it('should be possible to provide a custom Randomizer', () => {
- const customFaker = new Faker({
- locale: {},
- randomizer: {
- next: () => 0,
- seed: () => void 0,
- },
+ describe('constructor()', () => {
+ describe('locale', () => {
+ it('should throw error if no locales passed', () => {
+ expect(() => new Faker({ locale: [] })).toThrow(
+ new FakerError(
+ 'The locale option must contain at least one locale definition.'
+ )
+ );
+ });
+ });
+
+ describe('randomizer', () => {
+ it('should be possible to provide a custom Randomizer', () => {
+ const customFaker = new Faker({
+ locale: {},
+ randomizer: {
+ next: () => 0,
+ seed: () => void 0,
+ },
+ });
+
+ expect(customFaker.number.int()).toBe(0);
+ expect(customFaker.number.int()).toBe(0);
+ expect(customFaker.number.int()).toBe(0);
+ });
+ });
+
+ describe('seed', () => {
+ it('should be possible to provide an initial seed', () => {
+ const customFaker = new Faker({
+ locale: {},
+ seed: 12345,
+ });
+
+ expect(customFaker.number.int()).toBe(8373237378417847);
+ expect(customFaker.number.int()).toBe(2849657659447330);
+ expect(customFaker.number.int()).toBe(1656593383470774);
+
+ customFaker.seed(12345);
+
+ expect(customFaker.number.int()).toBe(8373237378417847);
+ expect(customFaker.number.int()).toBe(2849657659447330);
+ expect(customFaker.number.int()).toBe(1656593383470774);
});
+ });
- expect(customFaker.number.int()).toBe(0);
- expect(customFaker.number.int()).toBe(0);
- expect(customFaker.number.int()).toBe(0);
+ describe('randomizer+seed', () => {
+ it('should take apply both the randomizer and seed', () => {
+ const customFaker = new Faker({
+ locale: {},
+ randomizer: generateMersenne32Randomizer(67890),
+ seed: 12345,
+ });
+
+ expect(customFaker.number.int()).toBe(8373237322874880);
+ expect(customFaker.number.int()).toBe(8017800868134912);
+ expect(customFaker.number.int()).toBe(2849657711493120);
+
+ customFaker.seed(12345); // Retry with the expected seed
+
+ expect(customFaker.number.int()).toBe(8373237322874880);
+ expect(customFaker.number.int()).toBe(8017800868134912);
+ expect(customFaker.number.int()).toBe(2849657711493120);
+ });
});
});
diff --git a/test/internal/seed.spec.ts b/test/internal/seed.spec.ts
new file mode 100644
index 00000000..c3503524
--- /dev/null
+++ b/test/internal/seed.spec.ts
@@ -0,0 +1,11 @@
+import { describe, expect, it } from 'vitest';
+import { randomSeed } from '../../src/internal/seed';
+
+describe('seed', () => {
+ it('should generate a random seed', () => {
+ const actual = randomSeed();
+
+ expect(actual).toBeTypeOf('number');
+ expect(actual).not.toBe(randomSeed());
+ });
+});
diff --git a/test/simple-faker.spec.ts b/test/simple-faker.spec.ts
index aa6f00c1..02c860a5 100644
--- a/test/simple-faker.spec.ts
+++ b/test/simple-faker.spec.ts
@@ -1,6 +1,6 @@
import type { MockInstance } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
-import { SimpleFaker, simpleFaker } from '../src';
+import { generateMersenne32Randomizer, SimpleFaker, simpleFaker } from '../src';
import { keys } from '../src/internal/keys';
describe('simpleFaker', () => {
@@ -20,6 +20,60 @@ describe('simpleFaker', () => {
}
});
+ describe('constructor()', () => {
+ describe('randomizer', () => {
+ it('should be possible to provide a custom Randomizer', () => {
+ const customFaker = new SimpleFaker({
+ randomizer: {
+ next: () => 0,
+ seed: () => void 0,
+ },
+ });
+
+ expect(customFaker.number.int()).toBe(0);
+ expect(customFaker.number.int()).toBe(0);
+ expect(customFaker.number.int()).toBe(0);
+ });
+ });
+
+ describe('seed', () => {
+ it('should be possible to provide an initial seed', () => {
+ const customFaker = new SimpleFaker({
+ seed: 12345,
+ });
+
+ expect(customFaker.number.int()).toBe(8373237378417847);
+ expect(customFaker.number.int()).toBe(2849657659447330);
+ expect(customFaker.number.int()).toBe(1656593383470774);
+
+ customFaker.seed(12345); // Retry with the expected seed
+
+ expect(customFaker.number.int()).toBe(8373237378417847);
+ expect(customFaker.number.int()).toBe(2849657659447330);
+ expect(customFaker.number.int()).toBe(1656593383470774);
+ });
+ });
+
+ describe('randomizer+seed', () => {
+ it('should take apply both the randomizer and seed', () => {
+ const customFaker = new SimpleFaker({
+ randomizer: generateMersenne32Randomizer(67890),
+ seed: 12345,
+ });
+
+ expect(customFaker.number.int()).toBe(8373237322874880);
+ expect(customFaker.number.int()).toBe(8017800868134912);
+ expect(customFaker.number.int()).toBe(2849657711493120);
+
+ customFaker.seed(12345); // Retry with the expected seed
+
+ expect(customFaker.number.int()).toBe(8373237322874880);
+ expect(customFaker.number.int()).toBe(8017800868134912);
+ expect(customFaker.number.int()).toBe(2849657711493120);
+ });
+ });
+ });
+
// This is only here for coverage
// The actual test is in mersenne.spec.ts
describe('seed()', () => {