Skip to content

Commit 609932e

Browse files
dubzzzSimenB
authored andcommitted
Add fuzzing based tests in Jest (jestjs#8012)
* Automatic bug detection with property based testing * Switch to built-in fc.anything() * Disable Map into fc.anything() * Rewrite matcher.property.test in TS * Update properties following review * Ignore key ordering for Set and Map * Remove potentially unstable property * One spec by matcher for property based tests * Add missing copyright * Ignore __arbitraries__ * Remove unecessary comments
1 parent 5bad349 commit 609932e

8 files changed

+246
-1
lines changed

jest.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module.exports = {
3131
],
3232
testEnvironment: './packages/jest-environment-node',
3333
testPathIgnorePatterns: [
34+
'/__arbitraries__/',
3435
'/node_modules/',
3536
'/examples/',
3637
'/e2e/.*/__tests__',

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"eslint-plugin-react": "^7.1.0",
4040
"eslint-plugin-relay": "~0.0.19",
4141
"execa": "^1.0.0",
42+
"fast-check": "^1.12.0",
4243
"glob": "^7.1.1",
4344
"graceful-fs": "^4.1.15",
4445
"isbinaryfile": "^4.0.0",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
import fc from 'fast-check';
10+
11+
// settings for anything arbitrary
12+
export const anythingSettings = {
13+
key: fc.oneof(fc.string(), fc.constantFrom('k1', 'k2', 'k3')),
14+
withBoxedValues: true,
15+
// Issue #7975 have to be fixed before enabling the generation of Map
16+
withMap: false,
17+
// Issue #7975 have to be fixed before enabling the generation of Set
18+
withSet: false,
19+
};
20+
21+
// assertion settings
22+
export const assertSettings = {}; // eg.: {numRuns: 10000}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
import fc from 'fast-check';
10+
import {
11+
anythingSettings,
12+
assertSettings,
13+
} from './__arbitraries__/sharedSettings';
14+
15+
describe('toContain', () => {
16+
it('should always find the value when inside the array', () => {
17+
fc.assert(
18+
fc.property(
19+
fc.array(fc.anything(anythingSettings)),
20+
fc.array(fc.anything(anythingSettings)),
21+
fc.anything(anythingSettings).filter(v => !Number.isNaN(v)),
22+
(startValues, endValues, v) => {
23+
// Given: startValues, endValues arrays and v value (not NaN)
24+
expect([...startValues, v, ...endValues]).toContain(v);
25+
},
26+
),
27+
assertSettings,
28+
);
29+
});
30+
31+
it('should not find the value if it has been cloned into the array', () => {
32+
fc.assert(
33+
fc.property(
34+
fc.array(fc.anything(anythingSettings)),
35+
fc.array(fc.anything(anythingSettings)),
36+
fc.dedup(fc.anything(anythingSettings), 2),
37+
(startValues, endValues, [a, b]) => {
38+
// Given: startValues, endValues arrays
39+
// and [a, b] equal, but not the same values
40+
// with `typeof a === 'object && a !== null`
41+
fc.pre(typeof a === 'object' && a !== null);
42+
expect([...startValues, a, ...endValues]).not.toContain(b);
43+
},
44+
),
45+
assertSettings,
46+
);
47+
});
48+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
import fc from 'fast-check';
10+
import {
11+
anythingSettings,
12+
assertSettings,
13+
} from './__arbitraries__/sharedSettings';
14+
15+
describe('toContainEqual', () => {
16+
it('should always find the value when inside the array', () => {
17+
fc.assert(
18+
fc.property(
19+
fc.array(fc.anything(anythingSettings)),
20+
fc.array(fc.anything(anythingSettings)),
21+
fc.anything(anythingSettings),
22+
(startValues, endValues, v) => {
23+
// Given: startValues, endValues arrays and v any value
24+
expect([...startValues, v, ...endValues]).toContainEqual(v);
25+
},
26+
),
27+
assertSettings,
28+
);
29+
});
30+
31+
it('should always find the value when cloned inside the array', () => {
32+
fc.assert(
33+
fc.property(
34+
fc.array(fc.anything(anythingSettings)),
35+
fc.array(fc.anything(anythingSettings)),
36+
fc.dedup(fc.anything(anythingSettings), 2),
37+
(startValues, endValues, [a, b]) => {
38+
// Given: startValues, endValues arrays
39+
// and [a, b] identical values
40+
expect([...startValues, a, ...endValues]).toContainEqual(b);
41+
},
42+
),
43+
assertSettings,
44+
);
45+
});
46+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
import fc from 'fast-check';
10+
import {
11+
anythingSettings,
12+
assertSettings,
13+
} from './__arbitraries__/sharedSettings';
14+
15+
describe('toEqual', () => {
16+
it('should be reflexive', () => {
17+
fc.assert(
18+
fc.property(fc.dedup(fc.anything(anythingSettings), 2), ([a, b]) => {
19+
// Given: a and b identical values
20+
expect(a).toEqual(b);
21+
}),
22+
assertSettings,
23+
);
24+
});
25+
26+
it('should be symmetric', () => {
27+
const safeExpectEqual = (a, b) => {
28+
try {
29+
expect(a).toEqual(b);
30+
return true;
31+
} catch (err) {
32+
return false;
33+
}
34+
};
35+
fc.assert(
36+
fc.property(
37+
fc.anything(anythingSettings),
38+
fc.anything(anythingSettings),
39+
(a, b) => {
40+
// Given: a and b values
41+
// Assert: We expect `expect(a).toEqual(b)`
42+
// to be equivalent to `expect(b).toEqual(a)`
43+
expect(safeExpectEqual(a, b)).toBe(safeExpectEqual(b, a));
44+
},
45+
),
46+
{
47+
...assertSettings,
48+
examples: [
49+
[0, 5e-324], // Issue #7941
50+
// [
51+
// new Set([false, true]),
52+
// new Set([new Boolean(true), new Boolean(true)]),
53+
// ], // Issue #7975
54+
],
55+
},
56+
);
57+
});
58+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
import fc from 'fast-check';
10+
import {
11+
anythingSettings,
12+
assertSettings,
13+
} from './__arbitraries__/sharedSettings';
14+
15+
describe('toStrictEqual', () => {
16+
it('should be reflexive', () => {
17+
fc.assert(
18+
fc.property(fc.dedup(fc.anything(anythingSettings), 2), ([a, b]) => {
19+
// Given: a and b identical values
20+
expect(a).toStrictEqual(b);
21+
}),
22+
assertSettings,
23+
);
24+
});
25+
26+
it('should be symmetric', () => {
27+
const safeExpectStrictEqual = (a, b) => {
28+
try {
29+
expect(a).toStrictEqual(b);
30+
return true;
31+
} catch (err) {
32+
return false;
33+
}
34+
};
35+
fc.assert(
36+
fc.property(
37+
fc.anything(anythingSettings),
38+
fc.anything(anythingSettings),
39+
(a, b) => {
40+
// Given: a and b values
41+
// Assert: We expect `expect(a).toStrictEqual(b)`
42+
// to be equivalent to `expect(b).toStrictEqual(a)`
43+
expect(safeExpectStrictEqual(a, b)).toBe(safeExpectStrictEqual(b, a));
44+
},
45+
),
46+
assertSettings,
47+
);
48+
});
49+
});

yarn.lock

+21-1
Original file line numberDiff line numberDiff line change
@@ -5641,6 +5641,14 @@ fancy-log@^1.3.2:
56415641
parse-node-version "^1.0.0"
56425642
time-stamp "^1.0.0"
56435643

5644+
fast-check@^1.12.0:
5645+
version "1.12.0"
5646+
resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-1.12.0.tgz#bff7908aa357703f207ee1877ffcf4486a698aa0"
5647+
integrity sha512-r/zmvxG/8IsxyOzf9T6wnjJQjSLmymAETeRVfs4/0VzxBmyK48o871BRKmEIgEBLmrg9klh3e4A4BZc3yrH2QQ==
5648+
dependencies:
5649+
lorem-ipsum "~1.0.6"
5650+
pure-rand "^1.6.2"
5651+
56445652
fast-deep-equal@^2.0.1:
56455653
version "2.0.1"
56465654
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
@@ -8348,6 +8356,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4
83488356
dependencies:
83498357
js-tokens "^3.0.0 || ^4.0.0"
83508358

8359+
lorem-ipsum@~1.0.6:
8360+
version "1.0.6"
8361+
resolved "https://registry.yarnpkg.com/lorem-ipsum/-/lorem-ipsum-1.0.6.tgz#69e9ab02bbb0991915d71b5559fe016d526f013f"
8362+
integrity sha512-Rx4XH8X4KSDCKAVvWGYlhAfNqdUP5ZdT4rRyf0jjrvWgtViZimDIlopWNfn/y3lGM5K4uuiAoY28TaD+7YKFrQ==
8363+
dependencies:
8364+
minimist "~1.2.0"
8365+
83518366
loud-rejection@^1.0.0:
83528367
version "1.6.0"
83538368
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
@@ -8912,7 +8927,7 @@ minimist@0.0.8:
89128927
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
89138928
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
89148929

8915-
minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
8930+
minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0:
89168931
version "1.2.0"
89178932
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
89188933
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
@@ -10690,6 +10705,11 @@ punycode@^2.1.0, punycode@^2.1.1:
1069010705
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
1069110706
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
1069210707

10708+
pure-rand@^1.6.2:
10709+
version "1.6.2"
10710+
resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-1.6.2.tgz#90b3ae78efe36f7e6e27bfffedf934f77382e6e6"
10711+
integrity sha512-HNwHOH63m7kCxe0kWEe5jSLwJiL2N83RUUN8POniFuZS+OsbFcMWlvXgxIU2nwKy2zYG2bQan40WBNK4biYPRg==
10712+
1069310713
q@0.9.7:
1069410714
version "0.9.7"
1069510715
resolved "https://registry.yarnpkg.com/q/-/q-0.9.7.tgz#4de2e6cb3b29088c9e4cbc03bf9d42fb96ce2f75"

0 commit comments

Comments
 (0)