Open9

fast-checkを試す

ryo_kawamataryo_kawamata
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);
};
ryo_kawamataryo_kawamata
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]);
      }
    })
  );
});
ryo_kawamataryo_kawamata

失敗したプロパティを再度実行したい場合は、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"
	}
));
ryo_kawamataryo_kawamata

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)
      })
    )
  })
})