この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、JavaScript Testing FrameworkであるJestで、例外処理のテストを実装してみました。
やってみた
Jestでは、.toThrow()というMactherが用意されており、これを使用して例外処理の評価を行うことができます。
toThrowError()
は.toThrow()
のエイリアスです。クラス、エラーメッセージ、インスタンスオブジェクトなどを評価対象として指定できます。
example
function drinkFlavor(flavor) {
if (flavor == 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}
test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}
// Test that the error message says "yuck" somewhere: these are equivalent
expect(drinkOctopus).toThrowError(/yuck/);
expect(drinkOctopus).toThrowError('yuck');
// Test the exact error message
expect(drinkOctopus).toThrowError(/^yuck, octopus flavor$/);
expect(drinkOctopus).toThrowError(new Error('yuck, octopus flavor'));
// Test that we get a DisgustingFlavorError
expect(drinkOctopus).toThrowError(DisgustingFlavorError);
});
同期関数、非同期関数それぞれで.toThrow()
を実際に使ってみます。
同期関数の場合
まず、次のような同期関数(Sync Function)の場合。引数がhoge
の場合にError
をThrowします。
main.ts
export const main = (arg: string): string => {
try {
if (arg === 'hoge') {
throw new Error();
}
return arg;
} catch (e) {
throw new Error(arg);
}
};
テストを成功させてみる
まず成功パターンです。上記の同期関数を対象としたテストを.toThrow()
で記述します。
test/main.test.ts
import { main } from '../main';
describe('main', () => {
test('toThrow with class', () => {
expect(() => main('hoge')).toThrow(Error);
});
test('toThrow with message', () => {
expect(() => main('hoge')).toThrow('hoge');
});
test('toThrow with instance', () => {
expect(() => main('hoge')).toThrow(new Error('hoge'));
});
});
テストを実行すると、いずれのテストケースもパスさせられました。
npx jest main.test.ts
PASS test/main.test.ts (7.023 s)
main
✓ toThrow with class (4 ms)
✓ toThrow with message (1 ms)
✓ toThrow with instance
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 7.081 s
Ran all test suites matching /main.test.ts/i.
テストを失敗させてみる
次に失敗パターンです。.toThrow()
の引数をそれぞれ先程と変更しています。
import { main } from '../main';
describe('main', () => {
test('toThrow with class', () => {
expect(() => main('hoge')).toThrow(TypeError);
});
test('toThrow with message', () => {
expect(() => main('hoge')).toThrow('nyao');
});
test('toThrow with instance', () => {
expect(() => main('hoge')).toThrow(new Error('fuga'));
});
});
テストを実行すると、いずれのテストケースもフェイルさせられました。
$ npx jest main.test.ts
FAIL test/main.test.ts (5.774 s)
main
✕ toThrow with class (16 ms)
✕ toThrow with message (2 ms)
✕ toThrow with instance (3 ms)
● main › toThrow with class
expect(received).toThrow(expected)
Expected constructor: TypeError
Received constructor: Error
Received message: "hoge"
6 | return arg;
7 | } catch (e) {
> 8 | throw new Error(arg);
| ^
9 | }
10 | };
11 |
at Object.<anonymous>.exports.main (main.ts:8:11)
at test/main.test.ts:5:18
at Object.<anonymous> (node_modules/expect/build/toThrowMatchers.js:83:11)
at Object.throwingMatcher [as toThrow] (node_modules/expect/build/index.js:338:21)
at Object.<anonymous> (test/main.test.ts:5:32)
3 | describe('main', () => {
4 | test('toThrow with class', () => {
> 5 | expect(() => main('hoge')).toThrow(TypeError);
| ^
6 | });
7 |
8 | test('toThrow with message', () => {
at Object.<anonymous> (test/main.test.ts:5:32)
● main › toThrow with message
expect(received).toThrow(expected)
Expected substring: "nyao"
Received message: "hoge"
6 | return arg;
7 | } catch (e) {
> 8 | throw new Error(arg);
| ^
9 | }
10 | };
11 |
at Object.<anonymous>.exports.main (main.ts:8:11)
at test/main.test.ts:9:18
at Object.<anonymous> (node_modules/expect/build/toThrowMatchers.js:83:11)
at Object.throwingMatcher [as toThrow] (node_modules/expect/build/index.js:338:21)
at Object.<anonymous> (test/main.test.ts:9:32)
7 |
8 | test('toThrow with message', () => {
> 9 | expect(() => main('hoge')).toThrow('nyao');
| ^
10 | });
11 |
12 | test('toThrow with instance', () => {
at Object.<anonymous> (test/main.test.ts:9:32)
● main › toThrow with instance
expect(received).toThrow(expected)
Expected message: "fuga"
Received message: "hoge"
6 | return arg;
7 | } catch (e) {
> 8 | throw new Error(arg);
| ^
9 | }
10 | };
11 |
at Object.<anonymous>.exports.main (main.ts:8:11)
at test/main.test.ts:13:18
at Object.<anonymous> (node_modules/expect/build/toThrowMatchers.js:83:11)
at Object.throwingMatcher [as toThrow] (node_modules/expect/build/index.js:338:21)
at Object.<anonymous> (test/main.test.ts:13:32)
11 |
12 | test('toThrow with instance', () => {
> 13 | expect(() => main('hoge')).toThrow(new Error('fuga'));
| ^
14 | });
15 | });
16 |
at Object.<anonymous> (test/main.test.ts:13:32)
Test Suites: 1 failed, 1 total
Tests: 3 failed, 3 total
Snapshots: 0 total
Time: 5.834 s, estimated 6 s
Ran all test suites matching /main.test.ts/i.
非同期関数の場合
続いて、次のような非同期関数(Async Function)の場合。
async_main.ts
export const main = async (arg: string): Promise<string> => {
try {
if (arg === 'hoge') {
throw new Error();
}
return arg;
} catch (e) {
throw new Error(arg);
}
};
非同期関数の場合も使い方は同じとなりますが、Promiseを解決できるように.rejects
を付ける必要があります。
If you expect a promise to be rejected, use the .rejects matcher. It works analogically to the .resolves matcher. If the promise is fulfilled, the test will automatically fail.
test/async_main.test.ts
import { main } from '../async_main';
describe('main', () => {
test('Error toThrow', () => {
expect(() => main('hoge')).rejects.toThrow(Error);
});
});
テストを実行すると、パスさせられました。
$ npx jest async_main.test.ts
PASS test/async_main.test.ts
main
✓ Error toThrow (3 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.845 s, estimated 6 s
Ran all test suites matching /async_main.test.ts/i.
おわりに
JavaScript Testing FrameworkであるJestで、例外処理のテストをしてみました。
正常系のテストは実装するかと思いますが、エラー系のテストは面倒くさがって省いてしまうこともあると思います。しかし今回紹介したJestの.throw
を使えば簡単に実装できるので活用していきたいですね。
以上