Writing tests first forces you to think about the problem you’re solving. Writing property-based tests forces you to think way harder.
- Jessica Kerr (@jessitron)
function symDiff<T>(
x: Array<T>,
y: Array<T>
): Array<Array<T>> {
return [
_.flatten(x.filter((a) => !_.contains(y, a))),
_.flatten(y.filter((b) => !_.contains(x, b)))
];
}- x contains no elements and y contains no elements
- x contains an element and y contains no elements
- x contains no elements and y contains an element
- x contains the same elements as y
- x contains some elements but not all the elements of y
- y contains some elements but not all the elements of x
const emptyArrayOfNumbers: Array<number> =
[];
const arrayWithOneNumber = [1];
const myAdditionalNumber = 2;
const arrayWithAnAdditionalNumber =
[...arrayWithOneNumber,
myAdditonalNumber];describe('symDiff', () => {
it('should return two empty arrays if both inputs are empty', () => {
const [a, b] = symDiff(emptyArrayOfNumbers, emptyArrayOfNumbers);
expect(a).empty();
expect(b).empty()
});
it('should return all of x if y is empty', () => {
const [a, b] = symDiff(arrayOfOneNumber, emptyArrayOfNumbers);
expect(a).eql(arrayOfOneNumber);
expect(b).empty();
})
});- If our y is an empty array, the first element of our return will be x
- If our x is an empty array, the second element of our return will be y
- If our x and y are equal, we get back two empty arrays
describe('symDiff', () => {
it('satisfies the identity property for the first argument', () => {
const anArrayOfNumbers = someArrayOfNumbers();
const [a, b] = symDiff(anArrayOfNumbers, []);
expect(a).eql(anArrayOfNumber);
});
it('satisfies the identity property of the second argument', () => {
const anArrayOfNumbers = someArrayOfNumbers();
const [a, b] = symDiff([], anArrayOfNumbers);
expect(b).eql(anArrayOfNumbers);
}));
it('satisfies the identity property if the two arguments are identical', () => {
const anArrayOfNumbers = someArrayOfNumbers();
const [a, b] = symDiff(anArrayOfNumbers, anArrayOfNumbers);
expect(a).empty();
expect(b).empty();
});
});- x contains no elements and y contains no elements
- x contains an element and y contains no elements
- x contains no elements and y contains an element
- x contains the same elements as y
https://github.com/dubzzz/fast-check
const fc = require('fast-check');describe('symDiff', () => {
it('satisfies the identity property for the first argument', () => {
fc.assert(fc.property(fc.array(fc.integer()), (anArrayOfNumbers) => {
const [a, b] = symDiff(anArrayOfNumbers, []);
expect(a).eql(anArrayOfNumbers);
}));
});
it('satisfies the identity property of the second argument', () => {
fc.assert(fc.property(fc.array(fc.integer()), (anArrayOfNumbers) => {
const [a, b] = symDiff([], anArrayOfNumbers);
expect(b).eql(anArrayOfNumbers);
}));
});
it('satisfies the identity property if the two arguments are identical', () => {
fc.assert(fc.property(fc.array(fc.integer()), (anArrayOfNumbers) => {
const [a, b] = symDiff(anArrayOfNumbers, anArrayOfNumbers);
expect(a.length).eql(0);
expect(b.length).eql(0);
}));
});
});- it just ran 100 tests with different arrays of numbers and checked that our properties held
- it tried long arrays of numbers
- it tried short arrays of numbers
- it tried negative numbers
- it tried HUGE numbers
- it tried empty arrays
- knows edge cases and makes sure those are run
- arrays of numbers
fc.array(fc.integer()) - arrays of strings
fc.array(fc.unicode()) - arrays of your object
fc.array(myAwesomeObjectArbitrary())
long-string-with-a-snowman-☃fails, it figures out that☃is the cause
- isn’t meant to replace all example tests
- but can be helpful for testing polymorphic code
- helpful way to think about your code