Repository: dubzzz/fast-check
Pull request #1711 (2021-04-09 17:57:09)

✅ Rewrite tests dealing with integer
<!-- Why is this PR for? -->
<!-- Describe the reason why you opened this PR or give a link towards the associated issue. -->

## In a nutshell

❌ New feature
❌ Fix an issue
❌ Documentation improvement
✔️ Other: *tests*

(✔️: yes, ❌: no)

## Potential impacts

None


codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/dubzzz/fast-check/pr/1711/builds/118097) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 142ffe1063986154542ffb8ee283f68a5161a649:

|Sandbox| Source |
|--|--|
|[Vanilla](https://codesandbox.io/s/vanilla-5593q)| Configuration |
|[fast-check-examples](https://codesandbox.io/s/fast-check-examples-yw8km)| Configuration |

codecov[bot]: # [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/1711?src=pr&el=h1) Report
> Merging [#1711](https://codecov.io/gh/dubzzz/fast-check/pull/1711?src=pr&el=desc) (db0f611) into [main](https://codecov.io/gh/dubzzz/fast-check/commit/95c7f156e889cb808bfe8ee9575f5f2e69424259?el=desc) (95c7f15) will **increase** coverage by `0.11%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/dubzzz/fast-check/pull/1711/graphs/tree.svg?width=650&height=150&src=pr&token=fRi2fJN730)](https://codecov.io/gh/dubzzz/fast-check/pull/1711?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##             main    #1711      +/-   ##
==========================================
+ Coverage   96.38%   96.49%   +0.11%     
==========================================
  Files         102      102              
  Lines        3567     3565       -2     
  Branches      710      710              
==========================================
+ Hits         3438     3440       +2     
+ Misses        125      121       -4     
  Partials        4        4              
```

| Flag | Coverage Δ | |
|---|---|---|
| unit-tests | `96.49% <100.00%> (+0.11%)` | :arrow_up: |
| unit-tests-10.x-Linux | `96.49% <100.00%> (+0.11%)` | :arrow_up: |
| unit-tests-12.x-Linux | `?` | |
| unit-tests-14.x-Linux | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dubzzz/fast-check/pull/1711?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/arbitrary/\_internals/IntegerArbitrary.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1711/diff?src=pr&el=tree#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL0ludGVnZXJBcmJpdHJhcnkudHM=) | `93.87% <ø> (+11.52%)` | :arrow_up: |
| [src/arbitrary/integer.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1711/diff?src=pr&el=tree#diff-c3JjL2FyYml0cmFyeS9pbnRlZ2VyLnRz) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/nat.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1711/diff?src=pr&el=tree#diff-c3JjL2FyYml0cmFyeS9uYXQudHM=) | `100.00% <100.00%> (ø)` | |
| [...rc/check/arbitrary/definition/ConverterFromNext.ts](https://codecov.io/gh/dubzzz/fast-check/pull/1711/diff?src=pr&el=tree#diff-c3JjL2NoZWNrL2FyYml0cmFyeS9kZWZpbml0aW9uL0NvbnZlcnRlckZyb21OZXh0LnRz) | `92.85% <0.00%> (-4.77%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/1711?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/1711?src=pr&el=footer). Last update [95c7f15...db0f611](https://codecov.io/gh/dubzzz/fast-check/pull/1711?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/arbitrary/_internals/IntegerArbitrary.ts b/src/arbitrary/_internals/IntegerArbitrary.ts
index 5c429b6d..88a2ad1c 100644
--- a/src/arbitrary/_internals/IntegerArbitrary.ts
+++ b/src/arbitrary/_internals/IntegerArbitrary.ts
@@ -7,9 +7,6 @@ import { shrinkInteger } from './helpers/ShrinkInteger';
 
 /** @internal */
 export class IntegerArbitrary extends NextArbitrary<number> {
-  static MIN_INT: number = 0x80000000 | 0;
-  static MAX_INT: number = 0x7fffffff | 0;
-
   constructor(readonly min: number, readonly max: number) {
     super();
   }
diff --git a/src/arbitrary/integer.ts b/src/arbitrary/integer.ts
index 5c7c3991..6775a584 100644
--- a/src/arbitrary/integer.ts
+++ b/src/arbitrary/integer.ts
@@ -27,8 +27,8 @@ export interface IntegerConstraints {
  * @internal
  */
 function buildCompleteIntegerConstraints(constraints: IntegerConstraints): Required<IntegerConstraints> {
-  const min = constraints.min !== undefined ? constraints.min : IntegerArbitrary.MIN_INT;
-  const max = constraints.max !== undefined ? constraints.max : IntegerArbitrary.MAX_INT;
+  const min = constraints.min !== undefined ? constraints.min : -0x80000000;
+  const max = constraints.max !== undefined ? constraints.max : 0x7fffffff;
   return { min, max };
 }
 
diff --git a/src/arbitrary/nat.ts b/src/arbitrary/nat.ts
index 098607f0..e2a97d5e 100644
--- a/src/arbitrary/nat.ts
+++ b/src/arbitrary/nat.ts
@@ -42,7 +42,7 @@ function nat(max: number): ArbitraryWithContextualShrink<number>;
  */
 function nat(constraints: NatConstraints): ArbitraryWithContextualShrink<number>;
 function nat(arg?: number | NatConstraints): ArbitraryWithContextualShrink<number> {
-  const max = typeof arg === 'number' ? arg : arg && arg.max !== undefined ? arg.max : IntegerArbitrary.MAX_INT;
+  const max = typeof arg === 'number' ? arg : arg && arg.max !== undefined ? arg.max : 0x7fffffff;
   if (max < 0) {
     throw new Error('fc.nat value should be greater than or equal to 0');
   }


Test patch:
diff --git a/test/unit/arbitrary/IntegerArbitrary.spec.ts b/test/unit/arbitrary/IntegerArbitrary.spec.ts
deleted file mode 100644
index ffb594e0..00000000
--- a/test/unit/arbitrary/IntegerArbitrary.spec.ts
+++ /dev/null
@@ -1,512 +0,0 @@
-import * as fc from '../../../lib/fast-check';
-
-import { integer } from '../../../src/arbitrary/integer';
-import { nat } from '../../../src/arbitrary/nat';
-import { maxSafeNat } from '../../../src/arbitrary/maxSafeNat';
-import { maxSafeInteger } from '../../../src/arbitrary/maxSafeInteger';
-
-import * as genericHelper from '../check/arbitrary/generic/GenericArbitraryHelper';
-
-import * as stubRng from '../stubs/generators';
-import { generateOneValue } from '../check/arbitrary/generic/GenerateOneValue';
-import { buildShrinkTree, renderTree } from '../check/arbitrary/generic/ShrinkTree';
-
-const isStrictlySmallerInteger = (v1: number, v2: number) => Math.abs(v1) < Math.abs(v2);
-
-describe('IntegerArbitrary', () => {
-  describe('integer', () => {
-    it('Should not fail on single value range', () =>
-      fc.assert(
-        fc.property(fc.integer(), fc.nat(), (seed, value) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const g = integer(value, value).generate(mrng).value;
-          return g == value;
-        })
-      ));
-    it('Should not suggest input in shrinked values', () =>
-      fc.assert(
-        fc.property(fc.integer(), fc.integer(), fc.nat(), (seed, min, num) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const arb = integer(min, min + num);
-          const shrinkable = arb.generate(mrng);
-          return shrinkable.shrink().every((s) => s.value != shrinkable.value);
-        })
-      ));
-    it('Should shrink towards zero', () =>
-      fc.assert(
-        fc.property(fc.integer(), fc.integer(), fc.nat(), (seed, min, num) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const arb = integer(min, min + num);
-          const shrinkable = arb.generate(mrng);
-          return shrinkable.value >= 0
-            ? shrinkable.shrink().every((s) => s.value <= shrinkable.value)
-            : shrinkable.shrink().every((s) => s.value >= shrinkable.value);
-        })
-      ));
-    it('Should be able to call shrink multiple times', () =>
-      fc.assert(
-        fc.property(fc.integer(), fc.integer(), fc.nat(), (seed, min, num) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const arb = integer(min, min + num);
-          const shrinkable = arb.generate(mrng);
-          const s1 = [...shrinkable.shrink()].map((s) => s.value);
-          const s2 = [...shrinkable.shrink()].map((s) => s.value);
-          return s1.length === s2.length && s1.every((v, idx) => v === s2[idx]);
-        })
-      ));
-    it('Should always suggest one shrinked value if it can go towards zero', () =>
-      fc.assert(
-        fc.property(fc.integer(), fc.integer(), fc.nat(), (seed, min, num) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const arb = integer(min, min + num);
-          const shrinkable = arb.generate(mrng);
-          const v = shrinkable.value;
-          return (
-            (min > 0 && v === min) ||
-            (min + num < 0 && v === min + num) ||
-            v === 0 ||
-            [...shrinkable.shrink()].length > 0
-          );
-        })
-      ));
-    it('Should produce the same values for shrink on instance and on arbitrary', () =>
-      fc.assert(
-        fc.property(fc.integer(), fc.integer(), fc.nat(), (seed, min, num) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const arb = integer(min, min + num);
-          const shrinkable = arb.generate(mrng);
-          const shrinksInstance = [...shrinkable.shrink()].map((s) => s.value);
-          const shrinksArb = [...arb.shrink(shrinkable.value)];
-          return (
-            shrinksInstance.length === shrinksArb.length && shrinksInstance.every((v, idx) => v === shrinksArb[idx])
-          );
-        })
-      ));
-
-    const log2 = (v: number) => Math.log(v) / Math.log(2);
-
-    it('Should be able to bias strictly positive integers', () =>
-      fc.assert(
-        fc.property(fc.integer(), genericHelper.minMax(fc.integer(1, 0x7fffffff)), (seed, range) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const arb = integer(range.min, range.max).withBias(1); // 100% of bias - not recommended outside of tests
-          const g = arb.generate(mrng).value;
-          if (range.min === range.max) {
-            return g === range.min;
-          }
-          return (
-            (range.min <= g && g <= range.min + log2(range.max - range.min)) ||
-            (range.max - log2(range.max - range.min) <= g && g <= range.max)
-          );
-        })
-      ));
-    it('Should be able to bias strictly negative integers', () =>
-      fc.assert(
-        fc.property(fc.integer(), genericHelper.minMax(fc.integer(-0x80000000, -1)), (seed, range) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const arb = integer(range.min, range.max).withBias(1); // 100% of bias - not recommended outside of tests
-          const g = arb.generate(mrng).value;
-          if (range.min === range.max) {
-            return g === range.min;
-          }
-          return (
-            (range.min <= g && g <= range.min + log2(range.max - range.min)) ||
-            (range.max - log2(range.max - range.min) <= g && g <= range.max)
-          );
-        })
-      ));
-    it('Should be able to bias negative and positive integers', () =>
-      fc.assert(
-        fc.property(fc.integer(), fc.integer(-0x80000000, -1), fc.integer(1, 0x7fffffff), (seed, min, max) => {
-          const mrng = stubRng.mutable.fastincrease(seed);
-          const arb = integer(min, max).withBias(1); // 100% of bias - not recommended outside of tests
-          const g = arb.generate(mrng).value;
-          return (
-            (-log2(-min) <= g && g <= log2(max)) ||
-            (min <= g && g <= min + log2(-min)) ||
-            (max - log2(max) <= g && g <= max)
-          );
-        })
-      ));
-    it('Should throw when minimum number is greater than maximum one', () =>
-      fc.assert(
-        fc.property(fc.integer(), fc.integer(), (a, b) => {
-          fc.pre(a !== b);
-          if (a < b) expect(() => integer(b, a)).toThrowError();
-          else expect(() => integer(a, b)).toThrowError();
-        })
-      ));
-    describe('Given no constraints [between -2**31 and 2**31 -1]', () => {
-      genericHelper.isValidArbitrary(() => integer(), {
-        isStrictlySmallerValue: isStrictlySmallerInteger,
-        isValidValue: (g: number) => typeof g === 'number' && -0x80000000 <= g && g <= 0x7fffffff,
-      });
-    });
-    describe('Given minimal value only [between min and 2**31 -1]', () => {
-      genericHelper.isValidArbitrary((min: number) => integer({ min }), {
-        seedGenerator: fc.integer(),
-        isStrictlySmallerValue: isStrictlySmallerInteger,
-        isValidValue: (g: number, min: number) => typeof g === 'number' && min <= g && g <= 0x7fffffff,
-      });
-    });
-    describe('Given maximal value only [between -2**31 and max]', () => {
-      genericHelper.isValidArbitrary((maxValue: number) => integer(maxValue), {
-        seedGenerator: fc.integer(),
-        isStrictlySmallerValue: isStrictlySmallerInteger,
-        isValidValue: (g: number, maxValue: number) => typeof g === 'number' && -0x80000000 <= g && g <= maxValue,
-      });
-    });
-    describe('Given minimal and maximal values [between min and max]', () => {
-      genericHelper.isValidArbitrary(
-        (constraints: { min: number; max: number }) => integer(constraints.min, constraints.max),
-        {
-          seedGenerator: genericHelper.minMax(fc.integer()),
-          isStrictlySmallerValue: isStrictlySmallerInteger,
-          isValidValue: (g: number, constraints: { min: number; max: number }) => {
-            const out = typeof g === 'number' && constraints.min <= g && g <= constraints.max;
-            if (!out) throw new Error(fc.stringify({ g, constraints }));
-            return out;
-          },
-        }
-      );
-    });
-    describe('Still support older signatures', () => {
-      it('Should support fc.integer(max)', () => {
-        fc.assert(
-          fc.property(fc.integer(), fc.integer(), (seed, max) => {
-            const refArbitrary = integer({ max });
-            const otherArbitrary = integer(max);
-            expect(generateOneValue(seed, otherArbitrary)).toEqual(generateOneValue(seed, refArbitrary));
-          })
-        );
-      });
-      it('Should support fc.integer(min, max)', () => {
-        fc.assert(
-          fc.property(fc.integer(), genericHelper.minMax(fc.integer()), (seed, constraints) => {
-            const refArbitrary = integer(constraints);
-            const otherArbitrary = integer(constraints.min, constraints.max);
-            expect(generateOneValue(seed, otherArbitrary)).toEqual(generateOneValue(seed, refArbitrary));
-          })
-        );
-      });
-    });
-
-    describe('contextualShrinkableFor', () => {
-      it('Should shrink strictly positive value for positive range including zero', () => {
-        // Arrange
-        const arb = integer({ min: 0, max: 10 });
-
-        // Act
-        const tree = buildShrinkTree(arb.contextualShrinkableFor(8));
-        const renderedTree = renderTree(tree).join('\n');
-
-        // Assert
-        //   When there is no more option, the shrinker retry one time with the value
-        //   current-1 to check if something that changed outside (another value not itself)
-        //   may have changed the situation.
-        expect(renderedTree).toMatchInlineSnapshot(`
-          "8
-          ├> 0
-          ├> 4
-          |  ├> 2
-          |  |  └> 1
-          |  |     └> 0
-          |  └> 3
-          |     └> 2
-          |        ├> 0
-          |        └> 1
-          |           └> 0
-          ├> 6
-          |  └> 5
-          |     └> 4
-          |        ├> 0
-          |        ├> 2
-          |        |  └> 1
-          |        |     └> 0
-          |        └> 3
-          |           └> 2
-          |              ├> 0
-          |              └> 1
-          |                 └> 0
-          └> 7
-             └> 6
-                ├> 0
-                ├> 3
-                |  └> 2
-                |     └> 1
-                |        └> 0
-                └> 5
-                   └> 4
-                      └> 3
-                         ├> 0
-                         └> 2
-                            └> 1
-                               └> 0"
-        `);
-
-        // Remarks:
-        // * When we shrink 5 in path 8 > 6 > 5
-        //   we already now that 4 passed so we now that the smallest failing case
-        //   to look for is >= 5
-        // * Same thing when we shrink 6 in path 8 > 6
-        // * When we shrink 7 in path 8 > 7
-        //   we already now that 6 passed so we now that the smallest failing case
-        //   to look for is >= 7
-      });
-      it('Should shrink strictly positive value for range not including zero', () => {
-        // Arrange
-        const arb = integer({ min: 10, max: 20 });
-
-        // Act
-        const tree = buildShrinkTree(arb.contextualShrinkableFor(18));
-        const renderedTree = renderTree(tree).join('\n');
-
-        // Assert
-        //   As the range [10, 20] and the value 18
-        //   are just offset by +10 compared to the first case,
-        //   the rendered tree will be offset by 10 too
-        expect(renderedTree).toMatchInlineSnapshot(`
-          "18
-          ├> 10
-          ├> 14
-          |  ├> 12
-          |  |  └> 11
-          |  |     └> 10
-          |  └> 13
-          |     └> 12
-          |        ├> 10
-          |        └> 11
-          |           └> 10
-          ├> 16
-          |  └> 15
-          |     └> 14
-          |        ├> 10
-          |        ├> 12
-          |        |  └> 11
-          |        |     └> 10
-          |        └> 13
-          |           └> 12
-          |              ├> 10
-          |              └> 11
-          |                 └> 10
-          └> 17
-             └> 16
-                ├> 10
-                ├> 13
-                |  └> 12
-                |     └> 11
-                |        └> 10
-                └> 15
-                   └> 14
-                      └> 13
-                         ├> 10
-                         └> 12
-                            └> 11
-                               └> 10"
-        `);
-      });
-      it('Should shrink strictly negative value for negative range including zero', () => {
-        // Arrange
-        const arb = integer({ min: -10, max: 0 });
-
-        // Act
-        const tree = buildShrinkTree(arb.contextualShrinkableFor(-8));
-        const renderedTree = renderTree(tree).join('\n');
-
-        // Assert
-        //   As the range [-10, 0] and the value -8
-        //   are the opposite of first case, the rendered tree will be the same except
-        //   it contains opposite values
-        expect(renderedTree).toMatchInlineSnapshot(`
-          "-8
-          ├> 0
-          ├> -4
-          |  ├> -2
-          |  |  └> -1
-          |  |     └> 0
-          |  └> -3
-          |     └> -2
-          |        ├> 0
-          |        └> -1
-          |           └> 0
-          ├> -6
-          |  └> -5
-          |     └> -4
-          |        ├> 0
-          |        ├> -2
-          |        |  └> -1
-          |        |     └> 0
-          |        └> -3
-          |           └> -2
-          |              ├> 0
-          |              └> -1
-          |                 └> 0
-          └> -7
-             └> -6
-                ├> 0
-                ├> -3
-                |  └> -2
-                |     └> -1
-                |        └> 0
-                └> -5
-                   └> -4
-                      └> -3
-                         ├> 0
-                         └> -2
-                            └> -1
-                               └> 0"
-        `);
-      });
-    });
-
-    describe('shrinkableFor', () => {
-      // shrinkableFor is the legacy version of contextualShrinkableFor.
-      // It only takes a context telling if we already shrunk once or not.
-      // It proves too slow for deep shrink scenarios, thus contextualShrinkableFor
-      // took its place for faster shrinker.
-
-      it('Should shrink strictly positive value for positive range including zero', () => {
-        // Arrange
-        const arb = integer({ min: 0, max: 7 });
-
-        // Act
-        const tree = buildShrinkTree(arb.shrinkableFor(6));
-        const renderedTree = renderTree(tree).join('\n');
-
-        // Assert
-        expect(renderedTree).toMatchInlineSnapshot(`
-          "6
-          ├> 0
-          ├> 3
-          |  └> 2
-          |     └> 1
-          |        └> 0
-          └> 5
-             ├> 3
-             |  └> 2
-             |     └> 1
-             |        └> 0
-             └> 4
-                ├> 2
-                |  └> 1
-                |     └> 0
-                └> 3
-                   └> 2
-                      └> 1
-                         └> 0"
-        `);
-      });
-      it('Should shrink strictly positive value for range not including zero', () => {
-        // Arrange
-        const arb = integer({ min: 2, max: 9 });
-
-        // Act
-        const tree = buildShrinkTree(arb.shrinkableFor(8));
-        const renderedTree = renderTree(tree).join('\n');
-
-        // Assert
-        expect(renderedTree).toMatchInlineSnapshot(`
-          "8
-          ├> 2
-          ├> 5
-          |  └> 4
-          |     └> 3
-          |        └> 2
-          └> 7
-             ├> 5
-             |  └> 4
-             |     └> 3
-             |        └> 2
-             └> 6
-                ├> 4
-                |  └> 3
-                |     └> 2
-                └> 5
-                   └> 4
-                      └> 3
-                         └> 2"
-        `);
-      });
-      it('Should shrink strictly negative value for negative range including zero', () => {
-        // Arrange
-        const arb = integer({ min: -7, max: 0 });
-
-        // Act
-        const tree = buildShrinkTree(arb.shrinkableFor(-6));
-        const renderedTree = renderTree(tree).join('\n');
-
-        // Assert
-        expect(renderedTree).toMatchInlineSnapshot(`
-          "-6
-          ├> 0
-          ├> -3
-          |  └> -2
-          |     └> -1
-          |        └> 0
-          └> -5
-             ├> -3
-             |  └> -2
-             |     └> -1
-             |        └> 0
-             └> -4
-                ├> -2
-                |  └> -1
-                |     └> 0
-                └> -3
-                   └> -2
-                      └> -1
-                         └> 0"
-        `);
-      });
-    });
-  });
-  describe('maxSafeInteger', () => {
-    describe('Given no constraints [between MIN_SAFE_INTEGER and MAX_SAFE_INTEGER]', () => {
-      genericHelper.isValidArbitrary(() => maxSafeInteger(), {
-        isStrictlySmallerValue: isStrictlySmallerInteger,
-        isValidValue: (g: number) =>
-          typeof g === 'number' && g >= Number.MIN_SAFE_INTEGER && g <= Number.MAX_SAFE_INTEGER,
-      });
-    });
-  });
-  describe('nat', () => {
-    it('Should throw when the number is less than 0', () =>
-      fc.assert(
-        fc.property(fc.integer(Number.MIN_SAFE_INTEGER, -1), (n) => {
-          expect(() => nat(n)).toThrowError();
-        })
-      ));
-    describe('Given no constraints [between 0 and 2**31 -1]', () => {
-      genericHelper.isValidArbitrary(() => nat(), {
-        isStrictlySmallerValue: isStrictlySmallerInteger,
-        isValidValue: (g: number) => typeof g === 'number' && g >= 0 && g <= 0x7fffffff,
-      });
-    });
-    describe('Given maximal value only [between 0 and max]', () => {
-      genericHelper.isValidArbitrary((maxValue: number) => nat(maxValue), {
-        seedGenerator: fc.nat(),
-        isStrictlySmallerValue: isStrictlySmallerInteger,
-        isValidValue: (g: number, maxValue: number) => typeof g === 'number' && g >= 0 && g <= maxValue,
-      });
-    });
-    describe('Still support older signatures', () => {
-      it('Should support fc.nat(max)', () => {
-        fc.assert(
-          fc.property(fc.integer(), fc.nat(), (seed, max) => {
-            const refArbitrary = nat({ max });
-            const otherArbitrary = nat(max);
-            expect(generateOneValue(seed, otherArbitrary)).toEqual(generateOneValue(seed, refArbitrary));
-          })
-        );
-      });
-    });
-  });
-  describe('maxSafeNat', () => {
-    describe('Given no constraints [between 0 and MAX_SAFE_INTEGER]', () => {
-      genericHelper.isValidArbitrary(() => maxSafeNat(), {
-        isStrictlySmallerValue: isStrictlySmallerInteger,
-        isValidValue: (g: number) => typeof g === 'number' && g >= 0 && g <= Number.MAX_SAFE_INTEGER,
-      });
-    });
-  });
-});
diff --git a/test/unit/arbitrary/_internals/IntegerArbitrary.spec.ts b/test/unit/arbitrary/_internals/IntegerArbitrary.spec.ts
new file mode 100644
index 00000000..be5251c2
--- /dev/null
+++ b/test/unit/arbitrary/_internals/IntegerArbitrary.spec.ts
@@ -0,0 +1,496 @@
+import * as fc from '../../../../lib/fast-check';
+import { IntegerArbitrary } from '../../../../src/arbitrary/_internals/IntegerArbitrary';
+import { NextValue } from '../../../../src/check/arbitrary/definition/NextValue';
+import { fakeRandom } from '../../check/arbitrary/generic/RandomHelpers';
+import {
+  assertGenerateProducesCorrectValues,
+  assertGenerateProducesSameValueGivenSameSeed,
+  assertGenerateProducesValuesFlaggedAsCanGenerate,
+  assertShrinkProducesCorrectValues,
+  assertShrinkProducesSameValueWithoutInitialContext,
+  assertShrinkProducesStrictlySmallerValue,
+  assertShrinkProducesValuesFlaggedAsCanGenerate,
+} from '../../check/arbitrary/generic/NextArbitraryAssertions';
+import { buildNextShrinkTree, renderTree, walkTree } from '../../check/arbitrary/generic/ShrinkTree';
+
+import * as BiasNumericRangeMock from '../../../../src/arbitrary/_internals/helpers/BiasNumericRange';
+import * as ShrinkIntegerMock from '../../../../src/arbitrary/_internals/helpers/ShrinkInteger';
+import { Stream } from '../../../../src/stream/Stream';
+
+function beforeEachHook() {
+  jest.resetModules();
+  jest.restoreAllMocks();
+  fc.configureGlobal({ beforeEach: beforeEachHook });
+}
+beforeEach(beforeEachHook);
+
+describe('IntegerArbitrary', () => {
+  describe('generate', () => {
+    it('should never bias and generate the full range when biasFactor is not specified', () =>
+      fc.assert(
+        fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), fc.maxSafeInteger(), (a, b, c) => {
+          // Arrange
+          const [min, mid, max] = [a, b, c].sort((v1, v2) => v1 - v2);
+          const { instance: mrng, nextInt } = fakeRandom();
+          nextInt.mockReturnValueOnce(mid);
+
+          // Act
+          const arb = new IntegerArbitrary(min, max);
+          const out = arb.generate(mrng, undefined);
+
+          // Assert
+          expect(out.value).toBe(mid);
+          expect(nextInt).toHaveBeenCalledTimes(1);
+          expect(nextInt).toHaveBeenCalledWith(min, max);
+        })
+      ));
+
+    it('should not always bias values (expect 1 times over biasFreq) and still generate full range when unbiased', () =>
+      fc.assert(
+        fc.property(
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          (a, b, c, biasFactor) => {
+            // Arrange
+            const [min, mid, max] = [a, b, c].sort((v1, v2) => v1 - v2);
+            const { instance: mrng, nextInt } = fakeRandom();
+            nextInt.mockReturnValueOnce(2); // 1 means bias, all others are unbiased
+            nextInt.mockReturnValueOnce(mid);
+
+            // Act
+            const arb = new IntegerArbitrary(min, max);
+            const out = arb.generate(mrng, biasFactor);
+
+            // Assert
+            expect(out.value).toBe(mid);
+            expect(nextInt).toHaveBeenCalledTimes(2);
+            expect(nextInt).toHaveBeenCalledWith(1, biasFactor);
+            expect(nextInt).toHaveBeenCalledWith(min, max);
+          }
+        )
+      ));
+
+    it('should bias values (1 times over biasFreq) by using one of the ranges from biasNumericRange', () =>
+      fc.assert(
+        fc.property(
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          // Remark:
+          // Following biasNumericRange is not fully identical to the onces that would be provided.
+          // Range (in this stub) can be larger than the requested one. Not impacting from a unit-test point of view.
+          fc.array(
+            fc
+              .tuple(fc.maxSafeInteger(), fc.maxSafeInteger())
+              .map(([a, b]) => (a < b ? { min: a, max: b } : { min: b, max: a })),
+            { minLength: 1 }
+          ),
+          (a, b, c, biasFactor, mod, ranges) => {
+            // Arrange
+            const [min, mid, max] = [a, b, c].sort((v1, v2) => v1 - v2);
+            const { instance: mrng, nextInt } = fakeRandom();
+            nextInt.mockReturnValueOnce(1); // 1 means bias
+            if (ranges.length !== 1) {
+              nextInt.mockImplementationOnce((min, max) => min + (mod % (max - min + 1)));
+            }
+            nextInt.mockReturnValueOnce(mid); // Remark: this value will most of the time be outside of requested range
+            const biasNumericRange = (jest.spyOn(
+              BiasNumericRangeMock,
+              'biasNumericRange'
+            ) as unknown) as jest.SpyInstance<{ min: number; max: number }[], [number, number, () => number]>;
+            biasNumericRange.mockReturnValueOnce(ranges);
+
+            // Act
+            const arb = new IntegerArbitrary(min, max);
+            const out = arb.generate(mrng, biasFactor);
+
+            // Assert
+            expect(out.value).toBe(mid);
+            expect(biasNumericRange).toHaveBeenCalledTimes(1);
+            expect(biasNumericRange).toHaveBeenCalledWith(min, max, expect.any(Function));
+            if (ranges.length === 1) {
+              expect(nextInt).toHaveBeenCalledTimes(2);
+              expect(nextInt).toHaveBeenCalledWith(1, biasFactor);
+              expect(nextInt).toHaveBeenCalledWith(ranges[0].min, ranges[0].max);
+            } else {
+              expect(nextInt).toHaveBeenCalledTimes(3);
+              expect(nextInt).toHaveBeenCalledWith(1, biasFactor);
+              const secondNextIntParams = nextInt.mock.calls[1];
+              expect(secondNextIntParams[0]).toBeLessThan(0); // arbitrary is supposed to prefer the first entry
+              expect(secondNextIntParams[1]).toBe(ranges.length - 2); // other entries do not have any special treatments
+              // negative values for [0], positive value n means ranges[n+1]
+              const secondNextIntResult = nextInt.mock.results[1].value;
+              const selectedRange = secondNextIntResult < 0 ? 0 : secondNextIntResult + 1;
+              expect(nextInt).toHaveBeenCalledWith(ranges[selectedRange].min, ranges[selectedRange].max);
+            }
+          }
+        )
+      ));
+  });
+
+  describe('canGenerate', () => {
+    it('should always tells it can generate values included in the requested range', () =>
+      fc.assert(
+        fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), fc.maxSafeInteger(), (a, b, c) => {
+          // Arrange
+          const [min, mid, max] = [a, b, c].sort((v1, v2) => v1 - v2);
+
+          // Act
+          const arb = new IntegerArbitrary(min, max);
+          const out = arb.canGenerate(mid);
+
+          // Assert
+          expect(out).toBe(true);
+        })
+      ));
+
+    it('should always reject values outside of the requested range', () =>
+      fc.assert(
+        fc.property(
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          fc.maxSafeInteger(),
+          fc.constantFrom(...(['lower', 'higher'] as const)),
+          (a, b, c, position) => {
+            // Arrange
+            const s = [a, b, c].sort((v1, v2) => v1 - v2);
+            const [min, max, requested] = position === 'lower' ? [s[1], s[2], s[0]] : [s[0], s[1], s[2]];
+            fc.pre(requested !== min && requested !== max);
+
+            // Act
+            const arb = new IntegerArbitrary(min, max);
+            const out = arb.canGenerate(requested);
+
+            // Assert
+            expect(out).toBe(false);
+          }
+        )
+      ));
+
+    it.each`
+      requested
+      ${'1'}
+      ${1.5}
+      ${-0}
+      ${Number.NaN}
+      ${''}
+      ${{}}
+    `('should always reject non integral values like $requested', ({ requested }) => {
+      // Arrange
+      const min = 0;
+      const max = 100;
+
+      // Act
+      const arb = new IntegerArbitrary(min, max);
+      const out = arb.canGenerate(requested);
+
+      // Assert
+      expect(out).toBe(false);
+    });
+  });
+
+  describe('shrink', () => {
+    it('should always call shrink helper when no context provided', () =>
+      fc.assert(
+        fc.property(fc.maxSafeNat(), fc.maxSafeNat(), fc.maxSafeNat(), (a, b, c) => {
+          // Arrange
+          const [min, mid, max] = [a, b, c].sort((v1, v2) => v1 - v2);
+          const expectedShrinks = Stream.nil<NextValue<number>>();
+          const shrinkInteger = jest.spyOn(ShrinkIntegerMock, 'shrinkInteger');
+          shrinkInteger.mockReturnValueOnce(expectedShrinks);
+
+          // Act
+          const arb = new IntegerArbitrary(min, max);
+          const shrinks = arb.shrink(mid, undefined);
+
+          // Assert
+          expect(shrinks).toBe(expectedShrinks);
+          expect(shrinkInteger).toHaveBeenCalledTimes(1);
+          expect(shrinkInteger).toHaveBeenCalledWith(mid, expect.any(Number), true);
+        })
+      ));
+  });
+});
+
+describe('IntegerArbitrary (integration)', () => {
+  type Extra = { min: number; max: number };
+  const extraParameters: fc.Arbitrary<Extra> = fc
+    .tuple(fc.maxSafeInteger(), fc.maxSafeInteger())
+    .map(([a, b]) => (a < b ? { min: a, max: b } : { min: b, max: a }));
+
+  const isCorrect = (value: number, extra: Extra) =>
+    typeof value === 'number' &&
+    Number.isInteger(value) &&
+    !Object.is(value, -0) &&
+    extra.min <= value &&
+    value <= extra.max;
+
+  const isStrictlySmaller = (v1: number, v2: number) => Math.abs(v1) < Math.abs(v2);
+
+  const integerBuilder = (extra: Extra) => new IntegerArbitrary(extra.min, extra.max);
+
+  it('should generate the same values given the same seed', () => {
+    assertGenerateProducesSameValueGivenSameSeed(integerBuilder, { extraParameters });
+  });
+
+  it('should only generate correct values', () => {
+    assertGenerateProducesCorrectValues(integerBuilder, isCorrect, { extraParameters });
+  });
+
+  it('should recognize values that would have been generated using it during generate', () => {
+    assertGenerateProducesValuesFlaggedAsCanGenerate(integerBuilder, { extraParameters });
+  });
+
+  it('should shrink towards the same values given the same seed', () => {
+    assertGenerateProducesSameValueGivenSameSeed(integerBuilder, { extraParameters });
+  });
+
+  it('should be able to shrink without any context', () => {
+    assertShrinkProducesSameValueWithoutInitialContext(integerBuilder, { extraParameters });
+  });
+
+  it('should only shrink towards correct values', () => {
+    assertShrinkProducesCorrectValues(integerBuilder, isCorrect, { extraParameters });
+  });
+
+  it('should recognize values that would have been generated using it during shrink', () => {
+    assertShrinkProducesValuesFlaggedAsCanGenerate(integerBuilder, { extraParameters });
+  });
+
+  it('should shrink towards strictly smaller values', () => {
+    assertShrinkProducesStrictlySmallerValue(integerBuilder, isStrictlySmaller, { extraParameters });
+  });
+
+  describe('shrink', () => {
+    it('should build a mirrored version of the shrinking tree if we negate all the values', () =>
+      fc.assert(
+        fc.property(
+          fc.maxSafeInteger(),
+          fc.integer({ min: 0, max: 20 }), // larger trees might be too wide
+          fc.integer({ min: 0, max: 20 }),
+          (start, o1, o2) => {
+            // Arrange
+            fc.pre(start + o1 <= Number.MAX_SAFE_INTEGER);
+            fc.pre(start + o2 <= Number.MAX_SAFE_INTEGER);
+            const min = start;
+            const [mid, max] = o1 < o2 ? [min + o1, min + o2] : [min + o2, min + o1];
+            const arb = new IntegerArbitrary(min, max);
+            const arbNegate = new IntegerArbitrary(-max, -min);
+
+            // Act
+            const tree = buildNextShrinkTree(arb, new NextValue(mid));
+            const treeNegate = buildNextShrinkTree(arbNegate, new NextValue(-mid));
+            const flat: number[] = [];
+            const flatNegate: number[] = [];
+            walkTree(tree, (v) => flat.push(v));
+            walkTree(treeNegate, (v) => flatNegate.push(v !== 0 ? -v : 0));
+
+            // Assert
+            expect(flatNegate).toEqual(flat);
+          }
+        )
+      ));
+
+    it('should build an offset version of the shrinking tree if we offset all the values (keep every value >=0)', () =>
+      fc.assert(
+        fc.property(
+          fc.integer({ min: 0, max: Number.MAX_SAFE_INTEGER }),
+          fc.integer({ min: 0, max: 20 }), // larger trees might be too wide
+          fc.integer({ min: 0, max: 20 }),
+          fc.integer({ min: 0, max: Number.MAX_SAFE_INTEGER }),
+          (start, o1, o2, offset) => {
+            // Arrange
+            fc.pre(start + o1 + offset <= Number.MAX_SAFE_INTEGER);
+            fc.pre(start + o2 + offset <= Number.MAX_SAFE_INTEGER);
+            const min = start;
+            const [mid, max] = o1 < o2 ? [min + o1, min + o2] : [min + o2, min + o1];
+            const arb = new IntegerArbitrary(min, max);
+            const arbOffset = new IntegerArbitrary(min + offset, max + offset);
+
+            // Act
+            const tree = buildNextShrinkTree(arb, new NextValue(mid));
+            const treeOffset = buildNextShrinkTree(arbOffset, new NextValue(mid + offset));
+            const flat: number[] = [];
+            const flatOffset: number[] = [];
+            walkTree(tree, (v) => flat.push(v));
+            walkTree(treeOffset, (v) => flatOffset.push(v - offset));
+
+            // Assert
+            expect(flatOffset).toEqual(flat);
+          }
+        )
+      ));
+
+    it('should shrink strictly positive value for positive range including zero', () => {
+      // Arrange
+      const arb = new IntegerArbitrary(0, 10);
+
+      // Act
+      const tree = buildNextShrinkTree(arb, new NextValue(8));
+      const renderedTree = renderTree(tree).join('\n');
+
+      // Assert
+      //   When there is no more option, the shrinker retry one time with the value
+      //   current-1 to check if something that changed outside (another value not itself)
+      //   may have changed the situation.
+      expect(renderedTree).toMatchInlineSnapshot(`
+          "8
+          ├> 0
+          ├> 4
+          |  ├> 2
+          |  |  └> 1
+          |  |     └> 0
+          |  └> 3
+          |     └> 2
+          |        ├> 0
+          |        └> 1
+          |           └> 0
+          ├> 6
+          |  └> 5
+          |     └> 4
+          |        ├> 0
+          |        ├> 2
+          |        |  └> 1
+          |        |     └> 0
+          |        └> 3
+          |           └> 2
+          |              ├> 0
+          |              └> 1
+          |                 └> 0
+          └> 7
+             └> 6
+                ├> 0
+                ├> 3
+                |  └> 2
+                |     └> 1
+                |        └> 0
+                └> 5
+                   └> 4
+                      └> 3
+                         ├> 0
+                         └> 2
+                            └> 1
+                               └> 0"
+        `);
+
+      // Remarks:
+      // * When we shrink 5 in path 8 > 6 > 5
+      //   we already now that 4 passed so we now that the smallest failing case
+      //   to look for is >= 5
+      // * Same thing when we shrink 6 in path 8 > 6
+      // * When we shrink 7 in path 8 > 7
+      //   we already now that 6 passed so we now that the smallest failing case
+      //   to look for is >= 7
+    });
+
+    it('should shrink strictly positive value for range not including zero', () => {
+      // Arrange
+      const arb = new IntegerArbitrary(10, 20);
+
+      // Act
+      const tree = buildNextShrinkTree(arb, new NextValue(18));
+      const renderedTree = renderTree(tree).join('\n');
+
+      // Assert
+      //   As the range [10, 20] and the value 18
+      //   are just offset by +10 compared to the first case,
+      //   the rendered tree will be offset by 10 too
+      expect(renderedTree).toMatchInlineSnapshot(`
+          "18
+          ├> 10
+          ├> 14
+          |  ├> 12
+          |  |  └> 11
+          |  |     └> 10
+          |  └> 13
+          |     └> 12
+          |        ├> 10
+          |        └> 11
+          |           └> 10
+          ├> 16
+          |  └> 15
+          |     └> 14
+          |        ├> 10
+          |        ├> 12
+          |        |  └> 11
+          |        |     └> 10
+          |        └> 13
+          |           └> 12
+          |              ├> 10
+          |              └> 11
+          |                 └> 10
+          └> 17
+             └> 16
+                ├> 10
+                ├> 13
+                |  └> 12
+                |     └> 11
+                |        └> 10
+                └> 15
+                   └> 14
+                      └> 13
+                         ├> 10
+                         └> 12
+                            └> 11
+                               └> 10"
+        `);
+    });
+
+    it('should shrink strictly negative value for negative range including zero', () => {
+      // Arrange
+      const arb = new IntegerArbitrary(-10, 0);
+
+      // Act
+      const tree = buildNextShrinkTree(arb, new NextValue(-8));
+      const renderedTree = renderTree(tree).join('\n');
+
+      // Assert
+      //   As the range [-10, 0] and the value -8
+      //   are the opposite of first case, the rendered tree will be the same except
+      //   it contains opposite values
+      expect(renderedTree).toMatchInlineSnapshot(`
+          "-8
+          ├> 0
+          ├> -4
+          |  ├> -2
+          |  |  └> -1
+          |  |     └> 0
+          |  └> -3
+          |     └> -2
+          |        ├> 0
+          |        └> -1
+          |           └> 0
+          ├> -6
+          |  └> -5
+          |     └> -4
+          |        ├> 0
+          |        ├> -2
+          |        |  └> -1
+          |        |     └> 0
+          |        └> -3
+          |           └> -2
+          |              ├> 0
+          |              └> -1
+          |                 └> 0
+          └> -7
+             └> -6
+                ├> 0
+                ├> -3
+                |  └> -2
+                |     └> -1
+                |        └> 0
+                └> -5
+                   └> -4
+                      └> -3
+                         ├> 0
+                         └> -2
+                            └> -1
+                               └> 0"
+        `);
+    });
+  });
+});
diff --git a/test/unit/arbitrary/_internals/helpers/ShrinkInteger.spec.ts b/test/unit/arbitrary/_internals/helpers/ShrinkInteger.spec.ts
new file mode 100644
index 00000000..41aba033
--- /dev/null
+++ b/test/unit/arbitrary/_internals/helpers/ShrinkInteger.spec.ts
@@ -0,0 +1,122 @@
+import fc from '../../../../../lib/fast-check';
+import { shrinkInteger } from '../../../../../src/arbitrary/_internals/helpers/ShrinkInteger';
+
+describe('shrinkInteger', () => {
+  it('should always return empty stream when current equals target', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.boolean(), (value, tryAsap) => {
+        // Arrange / Act
+        const shrinks = [...shrinkInteger(value, value, tryAsap)];
+
+        // Assert
+        expect(shrinks).toHaveLength(0);
+      })
+    ));
+
+  it('should always starts stream with target when try asap is requested (when current not target)', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), (current, target) => {
+        // Arrange
+        fc.pre(current !== target);
+
+        // Act
+        const shrinks = [...shrinkInteger(current, target, true)];
+
+        // Assert
+        expect(shrinks).not.toHaveLength(0);
+        expect(shrinks[0].value).toBe(target);
+        expect(shrinks[0].context).toBe(undefined);
+      })
+    ));
+
+  it('should only include values between current and target in the stream', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), fc.boolean(), (current, target, tryAsap) => {
+        // Arrange / Act
+        const shrinks = [...shrinkInteger(current, target, tryAsap)];
+        const values = shrinks.map((v) => v.value);
+
+        // Assert
+        for (const v of values) {
+          expect(v).toBeGreaterThanOrEqual(Math.min(current, target));
+          expect(v).toBeLessThanOrEqual(Math.max(current, target));
+        }
+      })
+    ));
+
+  it('should never include current in the stream', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), fc.boolean(), (current, target, tryAsap) => {
+        // Arrange / Act
+        const shrinks = [...shrinkInteger(current, target, tryAsap)];
+        const values = shrinks.map((v) => v.value);
+
+        // Assert
+        expect(values).not.toContain(current);
+      })
+    ));
+
+  it('should never include target in the stream when try asap is not requested', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), (current, target) => {
+        // Arrange / Act
+        const shrinks = [...shrinkInteger(current, target, false)];
+        const values = shrinks.map((v) => v.value);
+
+        // Assert
+        expect(values).not.toContain(target);
+      })
+    ));
+
+  it('should always set context to be the value of previous entry in the stream', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), fc.boolean(), (current, target, tryAsap) => {
+        // Arrange / Act
+        const shrinks = [...shrinkInteger(current, target, tryAsap)];
+
+        // Assert
+        for (let idx = 1; idx < shrinks.length; ++idx) {
+          expect(shrinks[idx].context).toBe(shrinks[idx - 1].value);
+        }
+      })
+    ));
+
+  it('should specify first context of the stream to target if and only if no try asap, undefined otherwise', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), fc.boolean(), (current, target, tryAsap) => {
+        // Arrange
+        const expectedFirstContext = tryAsap ? undefined : target;
+
+        // Act
+        const first = shrinkInteger(current, target, tryAsap).getNthOrLast(0);
+
+        // Assert
+        if (first !== null) {
+          expect(first.context).toBe(expectedFirstContext);
+        }
+      })
+    ));
+
+  if (typeof BigInt === 'undefined') {
+    return;
+  }
+
+  it('should always strictly increase distance from target as we move in the stream', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), fc.boolean(), (current, target, tryAsap) => {
+        // Arrange / Act
+        const shrinks = [...shrinkInteger(current, target, tryAsap)];
+        const absDiff = (a: number, b: number): bigint => {
+          const result = BigInt(a) - BigInt(b);
+          return result >= 0 ? result : -result;
+        };
+
+        // Assert
+        for (let idx = 1; idx < shrinks.length; ++idx) {
+          const previousDistance = absDiff(shrinks[idx - 1].value, target);
+          const currentDistance = absDiff(shrinks[idx].value, target);
+          expect(currentDistance).toBeGreaterThan(previousDistance);
+        }
+      })
+    ));
+});
diff --git a/test/unit/arbitrary/integer.spec.ts b/test/unit/arbitrary/integer.spec.ts
new file mode 100644
index 00000000..abe0581d
--- /dev/null
+++ b/test/unit/arbitrary/integer.spec.ts
@@ -0,0 +1,165 @@
+import * as fc from '../../../lib/fast-check';
+import { integer } from '../../../src/arbitrary/integer';
+
+import { convertToNext } from '../../../src/check/arbitrary/definition/Converters';
+import { fakeNextArbitrary } from '../check/arbitrary/generic/NextArbitraryHelpers';
+
+import * as IntegerArbitraryMock from '../../../src/arbitrary/_internals/IntegerArbitrary';
+
+function fakeIntegerArbitrary() {
+  const instance = fakeNextArbitrary<number>().instance as IntegerArbitraryMock.IntegerArbitrary;
+  instance.defaultTarget = jest.fn();
+  return instance;
+}
+
+function beforeEachHook() {
+  jest.resetModules();
+  jest.restoreAllMocks();
+  fc.configureGlobal({ beforeEach: beforeEachHook });
+}
+beforeEach(beforeEachHook);
+
+describe('integer', () => {
+  it('should instantiate IntegerArbitrary(-0x80000000, 0x7fffffff) for integer()', () => {
+    // Arrange
+    const instance = fakeIntegerArbitrary();
+    const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+    IntegerArbitrary.mockImplementation(() => instance);
+
+    // Act
+    const arb = integer();
+
+    // Assert
+    expect(IntegerArbitrary).toHaveBeenCalledWith(-0x80000000, 0x7fffffff);
+    expect(convertToNext(arb)).toBe(instance);
+  });
+
+  it('should instantiate IntegerArbitrary(-0x80000000, 0x7fffffff) for integer({})', () => {
+    // Arrange
+    const instance = fakeIntegerArbitrary();
+    const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+    IntegerArbitrary.mockImplementation(() => instance);
+
+    // Act
+    const arb = integer({});
+
+    // Assert
+    expect(IntegerArbitrary).toHaveBeenCalledWith(-0x80000000, 0x7fffffff);
+    expect(convertToNext(arb)).toBe(instance);
+  });
+
+  it('should instantiate IntegerArbitrary(min, 0x7fffffff) for integer({min})', () =>
+    fc.assert(
+      fc.property(fc.integer({ min: Number.MIN_SAFE_INTEGER, max: 0x7fffffff }), (min) => {
+        // Arrange
+        const instance = fakeIntegerArbitrary();
+        const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+        IntegerArbitrary.mockImplementation(() => instance);
+
+        // Act
+        const arb = integer({ min });
+
+        // Assert
+        expect(IntegerArbitrary).toHaveBeenCalledWith(min, 0x7fffffff);
+        expect(convertToNext(arb)).toBe(instance);
+      })
+    ));
+
+  it('should instantiate IntegerArbitrary(-0x80000000, max) for integer({max})', () =>
+    fc.assert(
+      fc.property(fc.integer({ min: -0x80000000, max: Number.MAX_SAFE_INTEGER }), (max) => {
+        // Arrange
+        const instance = fakeIntegerArbitrary();
+        const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+        IntegerArbitrary.mockImplementation(() => instance);
+
+        // Act
+        const arb = integer({ max });
+
+        // Assert
+        expect(IntegerArbitrary).toHaveBeenCalledWith(-0x80000000, max);
+        expect(convertToNext(arb)).toBe(instance);
+      })
+    ));
+
+  it('should instantiate IntegerArbitrary(min, max) for integer({min, max})', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), (a, b) => {
+        // Arrange
+        const [min, max] = a < b ? [a, b] : [b, a];
+        const instance = fakeIntegerArbitrary();
+        const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+        IntegerArbitrary.mockImplementation(() => instance);
+
+        // Act
+        const arb = integer({ min, max });
+
+        // Assert
+        expect(IntegerArbitrary).toHaveBeenCalledWith(min, max);
+        expect(convertToNext(arb)).toBe(instance);
+      })
+    ));
+
+  it('[legacy] should instantiate IntegerArbitrary(-0x80000000, max) for integer(max)', () =>
+    fc.assert(
+      fc.property(fc.integer({ min: -0x80000000, max: Number.MAX_SAFE_INTEGER }), (max) => {
+        // Arrange
+        const instance = fakeIntegerArbitrary();
+        const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+        IntegerArbitrary.mockImplementation(() => instance);
+
+        // Act
+        const arb = integer({ max });
+
+        // Assert
+        expect(IntegerArbitrary).toHaveBeenCalledWith(-0x80000000, max);
+        expect(convertToNext(arb)).toBe(instance);
+      })
+    ));
+
+  it('[legacy] should instantiate IntegerArbitrary(min, max) for integer(min, max)', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), (a, b) => {
+        // Arrange
+        const [min, max] = a < b ? [a, b] : [b, a];
+        const instance = fakeIntegerArbitrary();
+        const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+        IntegerArbitrary.mockImplementation(() => instance);
+
+        // Act
+        const arb = integer({ min, max });
+
+        // Assert
+        expect(IntegerArbitrary).toHaveBeenCalledWith(min, max);
+        expect(convertToNext(arb)).toBe(instance);
+      })
+    ));
+
+  it('should throw when minimum value is greater than default maximum one', () =>
+    fc.assert(
+      fc.property(fc.integer({ min: 0x80000000, max: Number.MAX_SAFE_INTEGER }), (min) => {
+        // Arrange / Act / Assert
+        expect(() => integer({ min })).toThrowError();
+      })
+    ));
+
+  it('should throw when maximum value is lower than default minimum one', () =>
+    fc.assert(
+      fc.property(fc.integer({ min: Number.MIN_SAFE_INTEGER, max: -0x80000001 }), (max) => {
+        // Arrange / Act / Assert
+        expect(() => integer({ max })).toThrowError();
+      })
+    ));
+
+  it('should throw when minimum value is greater than maximum one', () =>
+    fc.assert(
+      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), (a, b) => {
+        // Arrange
+        fc.pre(a !== b);
+        const [low, high] = a < b ? [a, b] : [b, a];
+
+        // Act / Assert
+        expect(() => integer({ min: high, max: low })).toThrowError();
+      })
+    ));
+});
diff --git a/test/unit/arbitrary/maxSafeInteger.spec.ts b/test/unit/arbitrary/maxSafeInteger.spec.ts
new file mode 100644
index 00000000..bb334c1a
--- /dev/null
+++ b/test/unit/arbitrary/maxSafeInteger.spec.ts
@@ -0,0 +1,34 @@
+import { maxSafeInteger } from '../../../src/arbitrary/maxSafeInteger';
+
+import { convertToNext } from '../../../src/check/arbitrary/definition/Converters';
+import { fakeNextArbitrary } from '../check/arbitrary/generic/NextArbitraryHelpers';
+
+import * as IntegerArbitraryMock from '../../../src/arbitrary/_internals/IntegerArbitrary';
+
+function fakeIntegerArbitrary() {
+  const instance = fakeNextArbitrary<number>().instance as IntegerArbitraryMock.IntegerArbitrary;
+  instance.defaultTarget = jest.fn();
+  return instance;
+}
+
+function beforeEachHook() {
+  jest.resetModules();
+  jest.restoreAllMocks();
+}
+beforeEach(beforeEachHook);
+
+describe('maxSafeInteger', () => {
+  it('should instantiate IntegerArbitrary(MIN_SAFE_INTEGER, MAX_SAFE_INTEGER) for maxSafeInteger()', () => {
+    // Arrange
+    const instance = fakeIntegerArbitrary();
+    const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+    IntegerArbitrary.mockImplementation(() => instance);
+
+    // Act
+    const arb = maxSafeInteger();
+
+    // Assert
+    expect(IntegerArbitrary).toHaveBeenCalledWith(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
+    expect(convertToNext(arb)).toBe(instance);
+  });
+});
diff --git a/test/unit/arbitrary/maxSafeNat.spec.ts b/test/unit/arbitrary/maxSafeNat.spec.ts
new file mode 100644
index 00000000..b0aeb149
--- /dev/null
+++ b/test/unit/arbitrary/maxSafeNat.spec.ts
@@ -0,0 +1,34 @@
+import { maxSafeNat } from '../../../src/arbitrary/maxSafeNat';
+
+import { convertToNext } from '../../../src/check/arbitrary/definition/Converters';
+import { fakeNextArbitrary } from '../check/arbitrary/generic/NextArbitraryHelpers';
+
+import * as IntegerArbitraryMock from '../../../src/arbitrary/_internals/IntegerArbitrary';
+
+function fakeIntegerArbitrary() {
+  const instance = fakeNextArbitrary<number>().instance as IntegerArbitraryMock.IntegerArbitrary;
+  instance.defaultTarget = jest.fn();
+  return instance;
+}
+
+function beforeEachHook() {
+  jest.resetModules();
+  jest.restoreAllMocks();
+}
+beforeEach(beforeEachHook);
+
+describe('maxSafeInteger', () => {
+  it('should instantiate IntegerArbitrary(0, MAX_SAFE_INTEGER) for maxSafeInteger()', () => {
+    // Arrange
+    const instance = fakeIntegerArbitrary();
+    const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+    IntegerArbitrary.mockImplementation(() => instance);
+
+    // Act
+    const arb = maxSafeNat();
+
+    // Assert
+    expect(IntegerArbitrary).toHaveBeenCalledWith(0, Number.MAX_SAFE_INTEGER);
+    expect(convertToNext(arb)).toBe(instance);
+  });
+});
diff --git a/test/unit/arbitrary/nat.spec.ts b/test/unit/arbitrary/nat.spec.ts
new file mode 100644
index 00000000..dae9e775
--- /dev/null
+++ b/test/unit/arbitrary/nat.spec.ts
@@ -0,0 +1,92 @@
+import * as fc from '../../../lib/fast-check';
+import { nat } from '../../../src/arbitrary/nat';
+
+import { convertToNext } from '../../../src/check/arbitrary/definition/Converters';
+import { fakeNextArbitrary } from '../check/arbitrary/generic/NextArbitraryHelpers';
+
+import * as IntegerArbitraryMock from '../../../src/arbitrary/_internals/IntegerArbitrary';
+
+function fakeIntegerArbitrary() {
+  const instance = fakeNextArbitrary<number>().instance as IntegerArbitraryMock.IntegerArbitrary;
+  instance.defaultTarget = jest.fn();
+  return instance;
+}
+
+function beforeEachHook() {
+  jest.resetModules();
+  jest.restoreAllMocks();
+  fc.configureGlobal({ beforeEach: beforeEachHook });
+}
+beforeEach(beforeEachHook);
+
+describe('integer', () => {
+  it('should instantiate IntegerArbitrary(0, 0x7fffffff) for nat()', () => {
+    // Arrange
+    const instance = fakeIntegerArbitrary();
+    const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+    IntegerArbitrary.mockImplementation(() => instance);
+
+    // Act
+    const arb = nat();
+
+    // Assert
+    expect(IntegerArbitrary).toHaveBeenCalledWith(0, 0x7fffffff);
+    expect(convertToNext(arb)).toBe(instance);
+  });
+
+  it('should instantiate IntegerArbitrary(0, 0x7fffffff) for nat({})', () => {
+    // Arrange
+    const instance = fakeIntegerArbitrary();
+    const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+    IntegerArbitrary.mockImplementation(() => instance);
+
+    // Act
+    const arb = nat({});
+
+    // Assert
+    expect(IntegerArbitrary).toHaveBeenCalledWith(0, 0x7fffffff);
+    expect(convertToNext(arb)).toBe(instance);
+  });
+
+  it('should instantiate IntegerArbitrary(0, max) for nat({max})', () =>
+    fc.assert(
+      fc.property(fc.integer({ min: 0, max: Number.MAX_SAFE_INTEGER }), (max) => {
+        // Arrange
+        const instance = fakeIntegerArbitrary();
+        const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+        IntegerArbitrary.mockImplementation(() => instance);
+
+        // Act
+        const arb = nat({ max });
+
+        // Assert
+        expect(IntegerArbitrary).toHaveBeenCalledWith(0, max);
+        expect(convertToNext(arb)).toBe(instance);
+      })
+    ));
+
+  it('should instantiate IntegerArbitrary(0, max) for nat(max)', () =>
+    fc.assert(
+      fc.property(fc.integer({ min: 0, max: Number.MAX_SAFE_INTEGER }), (max) => {
+        // Arrange
+        const instance = fakeIntegerArbitrary();
+        const IntegerArbitrary = jest.spyOn(IntegerArbitraryMock, 'IntegerArbitrary');
+        IntegerArbitrary.mockImplementation(() => instance);
+
+        // Act
+        const arb = nat(max);
+
+        // Assert
+        expect(IntegerArbitrary).toHaveBeenCalledWith(0, max);
+        expect(convertToNext(arb)).toBe(instance);
+      })
+    ));
+
+  it('should throw when maximum value is lower than zero', () =>
+    fc.assert(
+      fc.property(fc.integer({ min: Number.MIN_SAFE_INTEGER, max: -1 }), (max) => {
+        // Arrange / Act / Assert
+        expect(() => nat({ max })).toThrowError();
+      })
+    ));
+});