🐱 First off, thanks for taking the time to contribute! 🐱
The following is a set of guidelines for contributing to fast-check and its packages. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
Feel free to contribute, ask questions, report bugs and issue pull requests
Before asking questions, please double-check you can not find your answer in one of the examples provided or in the documentation of the project:
- Documentation
- Examples provided inside the project
- Examples of properties
- Example: fuzzing a REST API
If nothing answered your question, please do not hesitate to create a new issue in GitHub.
You should report bugs using create a new issue in GitHub.
In order to start playing with the code locally you must run the following set of commands:
git clone https://github.com/dubzzz/fast-check.git && cd fast-check
yarn
yarn workspace fast-check build #compile the code in packages/fast-check/src, build the packages/fast-check/lib content
Once done, everything is ready for you to start working on the code.
Code style standard is enforced by Prettier.
Once done with your development you can check it follow the recommended code style by running yarn format:check
or run autofixes with yarn format
.
You should also check for linting by running yarn lint:check
and fix lint problems with yarn lint
.
All pull requests will trigger GitHub Actions checks. It ensures that the pull request follow the code style of the project and do not break existing tests.
Any change in the code may have impact in the next release. In order to ease our deployment process we try to anticipate as much as possible the impacts of each single pull request from a semver point of view:
- decline: really no impact code side from a bundle point of view
- patch: no visible impact, generally mostly fixing bugs or typos in the code
- minor: new features
- major: breaking changes
In order to ease this work, before opening the PR (or after if you forgot to do it), you should run the script yarn bump
to underline which packages have been impacted. Please note that our internals and private packages must always be toggled to 'decline' as we don't plan to bump their versions.
If you plan to update your PR with either a fix for the tests or change following code reviews please directly commit your new commit in your branch, PR will get updated automatically.
Before your fix:
--*---> main on dubzzz/fast-check
\
#1 branch-pr on your fork
After your fix:
--*---> main on dubzzz/fast-check
\
#1 --- #2 branch-pr on your fork
Ideally to resync your branch with main prefer a merge of main branch into your PR branch. It has the advantage to preserves the commit history on GitHub PR (contrary to rebase and force push).
✔️ Create a feature request
Before adding any new arbitrary into fast-check please make sure to fill a Feature request
to justify the need for such arbitrary.
✔️ Code the arbitrary
All the arbitraries defined by fast-check are available in src/arbitrary
.
Create a new file for the new one if it does not fit into the existing ones.
✔️ Test the arbitrary
Most of the newly added arbitraries will just be a combination of existing ones (mostly mapping from one entry to another). We expect a quite minimal amount of tests to be added as most of the logic depends on the built-in blocks.
- Unit-test & Integration - in
test/unit/arbitrary
import * as fc from 'fast-check';
import { myArbitrary } from '../../../../src/arbitrary/MyArbitrary';
import {
assertProduceCorrectValues,
assertProduceSameValueGivenSameSeed,
assertProduceValuesShrinkableWithoutContext,
assertShrinkProducesSameValueWithoutInitialContext,
assertShrinkProducesStrictlySmallerValue,
} from './__test-helpers__/NextArbitraryAssertions';
describe('myArbitrary', () => {
// Tests in isolation!
// You may want to check that generate, canShrinkWithoutContext and shrink
// are working as expecting given mocked or stubbed data (see our usage of spies).
});
describe('myArbitrary (integration)', () => {
// Tests in real life!
// In this section we assess that the arbitrary will work as expected by calling it with a real random generator
// and without mocking any of its underlyings. In order to do that we have an already predefined set of helpers.
// Among those helpers only some are really compulsory as they will ensure that the arbitrary does not break the
// rules. The other ones tend to have the best possible version of the arbitrary by ensuring the shrinker will
// always shrink towards strictly smaller values or that user defined values can be shrunk.
type Extra = /* Typing for the extra props received by myArbitraryBuilder */;
const extraParameters: fc.Arbitrary<Extra> = /* Arbitrary producing values for myArbitraryBuilder */;
const isCorrect = (value: /* Type of the value */, extra: Extra) => {
// Returns true if the value is correct given extra
// Returs false or throws (possibly via expect) if value is invalid
};
const isStrictlySmaller = (vNew: /* Type of the value */, vOld: /* Type of the value */, extra: Extra) => {
// Returns true if the vNew is really strictly smaller than vOld
// Returs false or throws (possibly via expect) otherwise
};
const myArbitraryBuilder = (extra: Extra) => convertToNext(myArbitrary(extra));
it('should produce the same values given the same seed', () => {
assertProduceSameValueGivenSameSeed(myArbitraryBuilder, { extraParameters });
});
it('should only produce correct values', () => {
assertProduceCorrectValues(myArbitraryBuilder, isCorrect, { extraParameters });
});
// OPTIONAL STEP
it('should produce values seen as shrinkable without any context', () => {
assertProduceValuesShrinkableWithoutContext(myArbitraryBuilder, { extraParameters });
});
// OPTIONAL STEP
it('should be able to shrink to the same values without initial context', () => {
assertShrinkProducesSameValueWithoutInitialContext(myArbitraryBuilder, { extraParameters });
});
// OPTIONAL STEP
it('should preserve strictly smaller ordering in shrink', () => {
assertShrinkProducesStrictlySmallerValue(myArbitraryBuilder, isStrictlySmaller, { extraParameters });
});
});
- No regression test - in
test/e2e/NoRegression.spec.ts
Then run yarn workspace fast-check e2e -- -u
locally to update the snapshot file. The NoRegression
spec is supposed to prevent unwanted breaking changes to be included in a future release of fast-check by taking a snapshot of the current output and enforcing it does not change over time (except if needed).
- Legacy support test - in
packages/test-minimal-support/main.js
The spec is responsible to check that most of the arbitraries provided by fast-check are working fine on rather old releases of node.
- Typing test - in
packages/test-types/main.ts
The spec is responsible to check that the typings are correct. It also ensures that they will not break with future changes or upcoming releases of TypeScript.
✔️ Document the arbitrary
-
Provide a minimal JSDoc on top of your new arbitrary and use the
/** @internal */
tag to hide internals - otherwise they would get published into the generated documentation -
Add the arbitrary into the list of Built-in Arbitraries - see https://fast-check.dev/docs/core-blocks/arbitraries/