テストの自動化をJavaScriptから行う方法を調査していたところ、Jestというテストフレームワークに出会いました。
試しに使用してみたところ、期待通りに動作したので、今回ご紹介したいと思います。
公式:https://jestjs.io/ja/docs/getting-started
インストール方法は以下
・npm
・Yarn
・pnpm
チュートリアルファイルを作成し、コードを実行したところ、以下の結果となりました。
試験内容: 1 + 2 は 3 という結果が返りますか?
training@1.0.0 test
jest
jest-haste-map: Haste module naming collision: countdown
The following files share their name; please adjust your hasteImpl:
* \chap17\countdown\package.json
* \chap17\practice\sec_end3\package.json
PASS ./sum.test.js
√ adds 1 + 2 to equal 3 (4 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---|---|---|---|---|---|
All files | 100 | 100 | 100 | 100 | |
sum.js | 100 | 100 | 100 | 100 | |
---------- | --------- | ---------- | --------- | --------- | ------------------- |
Test Suites: 1 passed, 1 total | |||||
Tests: 1 passed, 1 total | |||||
Snapshots: 0 total | |||||
Time: 2.067 s | |||||
Ran all test suites. |
試験にパスできたので、期待値通りですね。
Jestは英語のフレームワークですが、一部日本語にも対応しています。
読みやすさ向上のために日本語を活用するのも良いでしょう。
Jestでは様々な値をテストすることができます。
値をチェックする「Matchers」と呼ばれる機能があり、
今回は試験的にMatchersを作成しました。
・describe いくつかの関連するテストを1つのブロックとしてまとめるためのメソッド。
describe("テストブロックの説明(日本語が使えます)", () => {
//テストケースを書く//
});
test(it)実際のテストコードを記述するためのメソッド。describeメソッドの中に関連するテストケースを記述する。testの代わりにitも可。
describe("テストブロックの説明", () => {
test("テストケース1の説明", () => {
//テストコードを記述//
});
test("テストケース2の説明", () => {
//テストコードを記述//
});
});
・expect テスト結果を評価するためのメソッド。マッチャ関数とともに使用される。
test("Xの値がAになることの確認", () => {
expect(X).toBe(A)
});
//1+2が3となることが期待値
test('1+2が3になるかをテストする', () => {
expect(1 + 2).toBe(3);
});
//toBeというのががMatchersとなる
・正常
describe('たし算', () => {
it('1たす3は4です', () => {
const result = 1 + 3;
expect(result).toBe(4);
});
});
・誤ったもの
describe('ひき算', () => {
it('2ひく1は1です', () => {
const result = 2 - 11;
expect(result).toBe(2);
});
});
・スキップ
describe('わり算', () => {
it.skip('3わる3は1です', () => {
const result = 3 / 3;
expect(result).toBe(1);
});
});
・変数も扱える
describe('たし算', () => {
describe('変数を使います', () => {
let number_a = 100;
let number_b = 100;
// このテストファイルのすべてのテストが実行される前1回だけ実行される
beforeAll(() => {
number_a = number_a + 50;
});
// このテストファイルのすべてのテストが実行された後1回だけ実行される
afterAll(() => {
number_a = 100;
});
// このテストファイルにあるテスト(it)が実行される前に毎回実行される
beforeEach(() => {
number_b = number_b + 50;
});
// このテストファイルにあるテスト(it)が実行された後に毎回実行される
afterEach(() => {
number_b = number_b - 50;
});
it('100に50をたすと150です', () => {
expect(number_a).toBe(150);
});
it('100に50をたすと150です', () => {
expect(number_b).toBe(150);
});
});
});
・.not 検証を否定する
test('2+2が5にならないことをテストする', () => {
expect(2 + 2).not.toBe(5);
});
・toBeNull は null のみ一致します
test('nullになることをテストする', () => {
expect(null).toBeNull();
expect(null).not.toBeNull();//nullにならないこと
});
・toBeUndefined は undefined のみ一致します
test('undefinedになることをテストする', () => {
expect(undefined).toBeUndefined();//undefined
expect(undefined).not.toBeDefined();//undefinedではない
});
・toBeDefined は toBeUndefined の反対です
test('undefinedにならないことをテストする', () => {
expect(1).toBeUndefined();//undefined
expect(1).not.toBeDefined();//undefinedではない
});
//toBeTruthy は if ステートメントが真であるとみなすものに一致します
test('trueになることをテストする', () => {
expect(true).toBeTruthy();
expect(1).toBeTruthy();
expect('aaa').toBeTruthy();
});
・toBeFalsy は if ステートメントが偽であるとみなすものに一致します
test('falseになることをテストする', () => {
expect(false).toBeFalsy();
expect(undefined).toBeFalsy();
expect(null).toBeFalsy();
expect(0).toBeFalsy();
});
・toBeGreaterThan(number)数値の比較する
test('2+2の計算', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);//numberより大きくなることを検証します。〇〇より大きいなので同じ値を含みません。
expect(value).toBeGreaterThanOrEqual(3.5);//number以上になることを検証します。〇〇以上なので同じ値を含みます。
expect(value).toBeLessThan(5);//numberより小さくなることを検証します。〇〇より小さいなので同じ値を含みません。
expect(value).toBeLessThanOrEqual(4.5);//number以下になることを検証します。〇〇以下なので同じ値を含みます。
expect(value).toBe(4);// toBeは数値として同じ値であるか
expect(value).toEqual(4);//toEqualはとして同じ値であるか
});
・丸め誤差が原因で期待通りにならない場合は
test('浮動小数点数の加算', () => {
const value = 0.1 + 0.2;
expect(value).toBe(0.3);//このように書くと、丸め込み誤差が原因で期待通りに動作しない
expect(value).toBeCloseTo(0.3); // これならば正しく動く
});
・文字列に対して正規表現でマッチするかを検証します。
test('Iはありますか?', () => {
expect('team').not.toMatch(/I/);
});
test('stopは含まれているか', () => {
expect('Christoph').toMatch(/stop/);
});
・配列と反復可能なオブジェクト、 配列やオブジェクトの中にitemが含まれているかを検証するために使います。
const noodles = [
'そば',
'うどん',
'ラーメン',
'そうめん',
'にゅうめん',
];
test('そばが含まれているかをテストする', () => {
expect(noodles).toContain('そば');
expect(new Set(noodles)).toContain('そば');
});
・ある関数が呼び出し時に例外を投げることをテストする
function compileAndroidCode() {
throw new Error('例外が発生しました。');
}
test('例外が発生したかをテストする', () => {
expect(() => compileAndroidCode()).toThrow();
expect(() => compileAndroidCode()).toThrow(Error);
expect(() => compileAndroidCode()).toThrow('例外が発生しました');
});
上記のテストコードの結果は以下のようになりました
FAIL ./index.test.js
√ 2+2が5にならないことをテストする
× nullになることをテストする (1 ms)
√ undefinedになることをテストする (1 ms)
× undefinedにならないことをテストする (1 ms)
√ trueになることをテストする
√ falseになることをテストする (1 ms)
√ 2+2の計算 (2 ms)
× 浮動小数点数の加算 (1 ms)
√ Iはありますか? (1 ms)
√ stopは含まれているか (1 ms)
√ そばが含まれているかをテストする (1 ms)
√ 例外が発生したかをテストする (4 ms)
たし算
√ 1たす3は4です (3 ms)
変数を使います
√ 100に50をたすと150です (1 ms)
√ 100に50をたすと150です
ひき算
× 2ひく1は1です (3 ms)
わり算
○ skipped 3わる3は1です
● ひき算 › 2ひく1は1です
expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: -9
44 | it('2ひく1は1です', () => {
45 | const result = 2 - 11;
> 46 | expect(result).toBe(2);
| ^
47 | });
48 | });
49 |
at Object.toBe (index.test.js:46:21)
● nullになることをテストする
expect(received).not.toBeNull()
Received: null
95 | test('nullになることをテストする', () => {
96 | expect(null).toBeNull();
> 97 | expect(null).not.toBeNull();//nullにならないこと
| ^
98 | });
99 |
100 | //toBeUndefined は undefined のみ一致します
at Object.toBeNull (index.test.js:97:21)
● undefinedにならないことをテストする
expect(received).toBeUndefined()
Received: 1
106 | //toBeDefined は toBeUndefined の反対です
107 | test('undefinedにならないことをテストする', () => {
> 108 | expect(1).toBeUndefined();//undefined
| ^
109 | expect(1).not.toBeDefined();//undefinedではない
110 | });
111 |
at Object.toBeUndefined (index.test.js:108:14)
● 浮動小数点数の加算
expect(received).toBe(expected) // Object.is equality
Expected: 0.3
Received: 0.30000000000000004
140 | test('浮動小数点数の加算', () => {
141 | const value = 0.1 + 0.2;
> 142 | expect(value).toBe(0.3);//このように書くと、丸め込み誤差が原因で期待通りに動作しない
| ^
143 | expect(value).toBeCloseTo(0.3); // これならば正しく動く
144 | });
145 |
at Object.toBe (index.test.js:142:18)
Test Suites: 1 failed, 1 total
Tests: 4 failed, 1 skipped, 12 passed, 17 total
Snapshots: 0 total
Time: 0.889 s, estimated 1 s
Ran all test suites matching /index.test.js/i
日本語が使えることにより、成功・失敗の判別が非常に分かりやすくなりました。
Jestでは日本語が使えるので、積極的に活用していきたいです。
上記のMatchersだけでなく、公式ドキュメントには完全なリストが掲載されています。
https://jestjs.io/ja/docs/expect
現在は単に出力をテストしているだけですが、Jestには豊富な拡張機能が用意されているので、結合テストまでできるように挑戦したいですね。