Open9
fast-checkを試す
これをやってみる
const sortInternal = <T>(tab: T[], start: number, end: number, cmp: (a: T, b: T) => boolean): T[] => {
if(end - start < 2) return tab;
let pivot = start;
for(let idx = start + 1; idx < end; ++idx) {
if(!cmp(tab[start], tab[idx])) {
const prev = tab[++pivot];
tab[pivot] = tab[idx];
tab[idx] = prev
}
}
const prev = tab[pivot]
tab[pivot] = tab[start]
tab[start] = prev;
sortInternal(tab, start, pivot, cmp);
sortInternal(tab, pivot + 1, end, cmp);
return tab;
}
export const sort = <T>(tab: T[]): T[] => {
return sortInternal([...tab], 0, tab.length, (a, b) => a < b);
};
import { sort } from "../src/sort";
import * as fc from 'fast-check';
test('should contain the same items', () => {
const count = (tab: number[], element: number) => tab.filter(v => v === element).length;
fc.assert(
fc.property(fc.array(fc.integer()), data => {
const sorted = sort(data);
expect(sorted.length).toEqual(data.length);
for (const item of data) {
expect(count(sorted, item)).toEqual(count(data, item));
}
})
);
});
test('should produce ordered array', () => {
fc.assert(
fc.property(fc.array(fc.integer()), data => {
const sorted = sort(data);
for (let idx = 1; idx < sorted.length; ++idx) {
expect(sorted[idx - 1]).toBeLessThanOrEqual(sorted[idx]);
}
})
);
});
デフォルトでは100回実行される
失敗したプロパティを再度実行したい場合は、seedを使う
Error: Property failed after 1 tests (seed: 1527423434693, path: "0:0:0"): ["","",""]
Shrunk 2 time(s)
Got error: Property failed by returning false
test('the failing test', () => fc.assert(
fc.property(
// some arbitraries...
// check method
), { // seed and path taken from the error message
seed: 1527423434693,
path: "0:0:0"
}
));
エッジケースで失敗する関数を作って試してみるか
いろいろライブラリでも使われているのか
tips
FizzBuzzを解いてみた
関数
type FizzBuzzReturn = "FizzBuzz" | "Fizz" | "Buzz" | number
export const fizzBuzz = (num: number): FizzBuzzReturn => {
if(num % 3 === 0 && num % 5 === 0) {
return "FizzBuzz"
}
if(num % 3 === 0) {
return "Fizz"
}
if(num % 5 === 0) {
return "Buzz"
}
return num
}
テスト
describe('fizzBuzz', () => {
test('3の倍数でかつ5の倍数の場合はFizzBuzzと返す', () => {
fc.assert(
fc.property(fc.nat().map(n => n * 15), num => {
fc.pre(num !== 0)
expect(fizzBuzz(num)).toBe('FizzBuzz')
})
)
})
test('3の倍数でかつ5の倍数ではない場合はFizzと返す', () => {
fc.assert(
fc.property(fc.nat().map(n => n * 3), num => {
fc.pre(num !== 0)
fc.pre(num % 5 !== 0)
expect(fizzBuzz(num)).toBe('Fizz')
})
)
})
test('5の倍数でかつ3の倍数ではない場合はBuzzと返す', () => {
fc.assert(
fc.property(fc.nat().map(n => n * 5), num => {
fc.pre(num !== 0)
fc.pre(num % 3 !== 0)
expect(fizzBuzz(num)).toBe('Buzz')
})
)
})
test('3の倍数でも5の倍数でもない場合は引数をそのまま返す', () => {
fc.assert(
fc.property(fc.nat(), num => {
fc.pre(num % 3 !== 0)
fc.pre(num % 5 !== 0)
expect(fizzBuzz(num)).toBe(num)
})
)
})
})