Chaiとは?
ChaiはNode.jsの環境で使われるアサーションライブラリの一つです。
アサーションライブラリは、テストを記述する際に「期待される結果」を簡単に表現するためのツールで、テストが成功か失敗かを判断するために使われます。
主にMochaというテストランナー(テストランナーは、テストスクリプトを実行し、その結果を表示してくれるツール)と一緒に使用されることが多いです。
Mocha自体は出力結果を確認するアサーション機能を保持していないため、MochaとChaiが一緒に使われます。なおChaiは他のテストフレームワークと組み合わせて使用することもできます。
ChaiとMochaを使ったコードの一例は下記です。
const chai = require('chai');
const assert = chai.assert;
describe('Array', function() {
it('should start empty', function() {
const arr = [];
assert.equal(arr.length, 0); // Chaiのassertを使って評価
});
});
上記コードのdescribeやitはmochaにより提供されます。
describeはテストのグループ化を行うための機能で、引数にはテストグループの名前と、グループ内で実行するテストケースを記述した関数(コールバック)を設定します。
itにはテストケースの説明(何をテストしているか)と、そのテストの実行内容(コールバック関数)を記述します。
一方でassertはChaiによって提供される機能であり、今回の例ではリストの長さが0かどうかを確認する処理がChaiによって記述されています。
AssertとExpectおよびShouldの違い
AssertとExpectおよびShouldはいずれもChaiで期待される値が出力されているかどうかを確認するためのツールです。
それぞれの違いは下記の通りです。
Assertとは?
Assertはシンプルで関数的な書き方をする記載方法です。
引数は(実際の値, 期待する値, エラーメッセージ)の順で記載します。
const assert = require('chai').assert;
assert.equal(5, 5, '値が一致する');
assert.strictEqual(5, 5, '厳密に一致する');
assert.isTrue(true, '値がtrueである');
assert.isArray([1, 2, 3], 'これは配列である');
assert.lengthOf([1, 2, 3], 3, '配列の長さは3である');Expectとは?
expect は 自然言語に近い文法で書ける スタイルで、メソッドチェーン(to, be, equal, have, with など)を使用して可読性の高いアサーションを作成できます。
expect(実際の値).to.メソッド(期待する値) の形式で記載します。
メソッドチェーンの詳細は「chainable getters一覧」の章を参照ください。
const expect = require('chai').expect;
expect(5).to.equal(5);
expect('hello').to.be.a('string');
expect([1, 2, 3]).to.have.lengthOf(3);
expect({ a: 1 }).to.have.property('a');
expect([1, 2, 3]).to.include(2);Shouldとは?
Shouldはオブジェクトや変数の後続に期待する相対を記載するスタイルであり、Expectと同様に可読性が高い記述方法です。
実際の値.should.メソッド(期待する値) の形式します。
const should = require('chai').should(); // `.should()` を実行して有効化
(5).should.equal(5);
'hello'.should.be.a('string');
[1, 2, 3].should.have.lengthOf(3);
({ a: 1 }).should.have.property('a');
[1, 2, 3].should.include(2);should はnull や undefined には使えないことに注意が必要です。例えば下記はエラーとなります。
let foo;
foo.should.equal(5); // エラー発生!どのように使い分けるか?
基本は自身が所属するプロジェクトの規約に沿ったものを使いましょう。
どのスタイルで記載してもよいですが、特に理由がなければ可読性が高いExpectを使うようにすると良いでしょう。
chainable getters一覧
「chainable getters」(チェーン可能なゲッター)とは、アサーションの読みやすさを向上させるために提供されているメソッドです。
これらは、expectやshouldスタイルで使用されるキーワードで、アサーションをチェーンする際に使うことができます。
「chainable getters」を使うことで、テストコードがより自然な言語のように書けるようになり、アサーションの意味が分かりやすくなります。以下に、各ゲッターの役割と使い方を解説します。
1. to
toは最も基本的なチェーンゲッターで、ほとんどのアサーションメソッドに必要です。expect(value).toの後に、比較や条件を指定するメソッドをチェーンします。
expect(5).to.be.a('number');2. be
beは、オブジェクトの状態を確認するアサーションに使います。例えば、to.be.trueやto.be.equal()など、条件を評価する際に使用されます。
expect(true).to.be.true;
expect(5).to.be.equal(5);3. been
beenは、過去の状態や結果に対して評価を行う際に使います。例えば、非同期テストで状態が変化していないことを確認する場合に使います。
expect(obj).to.have.been.calledOnce;4. is
isは、状態やプロパティの検証に使用されます。isを使うことで、ある特定の条件が満たされているかどうかをチェックします。
expect('hello').to.be.is.a('string');5. that
thatは、より詳細なアサーションのために使います。expectと合わせて、詳細な検証を行いたい時に使います。
expect(foo).to.be.that.deep.equal(bar);6. which
whichは、特定のプロパティや特徴を確認する際に使用されます。オブジェクトのプロパティに対するアサーションを行う際に便利です。
expect(obj).to.have.property('name').which.is.a('string');7. and
andは、複数の条件を組み合わせて評価する際に使用されます。複数のアサーションを一度に行いたい場合に有効です。
expect(5).to.be.above(3).and.below(10);8. has
hasは、オブジェクトが特定のプロパティを持っているかを確認するために使います。
expect({name: 'John'}).to.has.property('name');9. have
haveは、オブジェクトや配列が特定の特徴を持っているかを確認する際に使用されます。特に、プロパティやアイテム数の確認に使います。
expect([1, 2, 3]).to.have.lengthOf(3);10. with
withは、オブジェクトや配列に特定の値が含まれているかを確認する際に使用されます。
11. at
atは、配列やコレクションにおける特定の位置にある値を評価する際に使います。
12. of
ofは、オブジェクトや配列の特定の要素が正しいかを確認する際に使います。
13. same
sameは、二つの値が厳密に同じかどうかをチェックします。通常、equalと同じですが、equalは型の違いも考慮しませんが、sameは型も含めて評価します。
expect([1, 2]).to.have.same.members([1, 2]);14. but
butは、特定の条件に追加の制約を設ける際に使います。
expect(5).to.be.above(3).but.not.equal(5);15. does
doesは、特定の動作が行われたかどうかを確認するために使います。例えば、メソッドが呼び出されたかなどを確認します。
expect(obj).to.have.property('foo').that.does.not.throw();16. still
stillは、値が変化していないかを確認する際に使います。例えば、非同期のテストで、あるプロパティの変更が起きていないかどうかを確認する場合に使います。
expect(foo).to.have.property('bar').still.equal('baz');17. also
alsoは、別のアサーションをチェーンする際に使います。複数の条件を同時に確認する際に有用です。
expect(foo).to.be.a('string').also.to.have.lengthOf(3);18. not
notは、否定的なアサーションに使用します。条件が成立しない場合をテストします。
expect(foo).to.not.be.empty;Matcher一覧
Matcher(マッチャー) とは、テストコードの中で 値が特定の条件を満たしているかを検証するためのメソッドです。
Chai では expect(), should, assert のいずれかを使ってアサーション(テストの検証)を行い、その際に Matcher を使って 期待する条件 を指定します。
値の比較で使われるMatcher
値の比較で使われるMatcherは下記です。
.equal(value)
.equal() は 値が完全に一致しているか(型も含めて) を確認します。JavaScript の === と同じ動作をするため、型が異なると false になります。
expect(2 + 2).to.equal(4); // ✅ OK (2 + 2 は 4 に等しい)
expect("hello").to.equal("hello"); // ✅ OK (文字列が同じ)
expect(true).to.equal(true); // ✅ OK (ブール値が同じ)
// NG の例(型が異なるため)
expect(2).to.equal("2"); // ❌ 型が違うのでエラー.deep.equal(value)
.deep.equal() は オブジェクトや配列の内容が等しいか をチェックします。equal() はオブジェクトの参照を比較するため、内容が同じでも false になりますが、deep.equal() は 各プロパティの値まで比較 します。
expect({ a: 1 }).to.deep.equal({ a: 1 }); // ✅ OK (オブジェクトのプロパティが一致)
// 配列の比較
expect([1, 2, 3]).to.deep.equal([1, 2, 3]); // ✅ OK (配列の内容が一致)
// NG の例(異なるオブジェクト参照)
const obj1 = { a: 1 };
const obj2 = obj1; // 参照が同じ
const obj3 = { a: 1 }; // 内容は同じだが別のオブジェクト
expect(obj1).to.equal(obj2); // ✅ OK (同じ参照)
expect(obj1).to.equal(obj3); // ❌ エラー(参照が違うため)
expect(obj1).to.deep.equal(obj3); // ✅ OK(内容が同じ).eql(value)
.eql() は .deep.equal() と 全く同じ動作 をするエイリアス(別名)です。どちらを使っても問題ありません。
expect({ a: 1 }).to.eql({ a: 1 }); // ✅ OK
expect([1, 2, 3]).to.eql([1, 2, 3]); // ✅ OK
// `.deep.equal` と同じ動作
expect({ a: 1 }).to.deep.equal({ a: 1 }); // ✅ OK
expect({ a: 1 }).to.eql({ a: 1 }); // ✅ OK(`deep.equal` のエイリアス).above(value) / .greaterThan(value)
.above() は 指定した値より大きいか を確認します。数値の大小比較を行う際に使用します。
expect(10).to.be.above(5); // ✅ OK (10 > 5)
expect(100).to.be.greaterThan(50); // ✅ OK (100 > 50)
// NG の例(10 は 10 より大きくない)
expect(10).to.be.above(10); // ❌ エラー.least(value)
.least() は 指定した値以上か(>=) を確認します。同じ値を含める のが .above() との違いです。
expect(10).to.be.at.least(10); // ✅ OK (10 >= 10)
expect(20).to.be.at.least(10); // ✅ OK (20 >= 10)
// NG の例(9 は 10 以上ではない)
expect(9).to.be.at.least(10); // ❌ エラー.below(value) / .lessThan(value)
.below() は 指定した値より小さいか を確認します。above() の逆バージョンです。
expect(5).to.be.below(10); // ✅ OK (5 < 10)
expect(3).to.be.lessThan(5); // ✅ OK (3 < 5)
// NG の例(5 は 5 未満ではない)
expect(5).to.be.below(5); // ❌ エラー.most(value)
.most() は 指定した値以下か(<=) を確認します。同じ値を含める のが .below() との違いです。
expect(5).to.be.at.most(5); // ✅ OK (5 <= 5)
expect(6).to.be.at.most(5); // ❌ 6 は 5 以下ではないのでエラー真偽値チェックで使われるMatcher
真偽値チェックで使われるMatcherは下記です。
.true
.true は、値が true であるか を確認するために使用します。
expect(true).to.be.true; // ✅ OK
expect(false).to.be.true; // ❌ エラー.false
.false は、値が false であるか を確認します。
expect(false).to.be.false; // ✅ OK
expect(true).to.be.false; // ❌ エラー.ok
.ok は、値が真であるか(true や真の値であるか)をチェックします。true や他の真の値(非 null, 非 undefined, 非空文字列など)であれば true と判定されます。
expect(1).to.be.ok; // ✅ OK (1 は真)
expect(false).to.be.ok; // ❌ エラー.not.ok
.not.ok は、値が偽であるか(false, null, undefined, 空文字列など)を確認します。
expect(0).to.not.be.ok; // ✅ OK (0 は偽)
expect(null).to.not.be.ok; // ✅ OK (null は偽)
expect(1).to.not.be.ok; // ❌ エラー.be.a(‘boolean’)
.be.a('boolean') は、値の型が boolean かどうか を確認します。
expect(true).to.be.a('boolean'); // ✅ OK
expect("true").to.be.a('boolean'); // ❌ エラー(文字列はbooleanではない)型チェックで使われるMatcher
型チェックで使われるMatcherは下記です。
.a(type)
.a(type) は、値が指定された型(例えば string, number, object など)であるか を確認します。
expect(42).to.be.a('number'); // ✅ OK
expect("Hello").to.be.a('string'); // ✅ OK
expect(true).to.be.a('boolean'); // ✅ OK.an(type)
.an(type) は、.a(type) と同じ役割を持ちますが、英語の表現の流れを重視して、a の代わりに an を使います。
expect(42).to.be.an('number'); // ✅ OK
expect("Hello").to.be.an('string'); // ✅ OK
expect([1, 2, 3]).to.be.an('array'); // ✅ OK.instanceOf(constructor)
.instanceOf(constructor) は、オブジェクトが指定されたクラスまたはコンストラクタのインスタンスであるか をチェックします。
class MyClass {}
const obj = new MyClass();
expect(obj).to.be.instanceOf(MyClass); // ✅ OK
expect({}).to.be.instanceOf(Object); // ✅ OK.typeof(type)
.typeof(type) は、値が指定された型の文字列として表現されるか をチェックします。
expect(42).to.have.a.property('typeof', 'number'); // ✅ OK
expect('Hello').to.have.a.property('typeof', 'string'); // ✅ OK
expect(true).to.have.a.property('typeof', 'boolean'); // ✅ OK.lengthOf(length)
.lengthOf(length) は、配列や文字列の長さが指定された値と一致するか を確認します。
expect([1, 2, 3]).to.have.lengthOf(3); // ✅ OK
expect("Hello").to.have.lengthOf(5); // ✅ OK
expect([].length).to.have.lengthOf(0); // ✅ OK配列・オブジェクトのチェックで使われるMatcher
配列・オブジェクトのチェックで使われるMatcherは下記です。
include(value)
.include() は、配列やオブジェクトが指定された値を含んでいるか をチェックします。配列の場合、指定した要素が含まれているかを確認し、オブジェクトの場合、指定したプロパティが存在するかを確認します。
expect([1, 2, 3]).to.include(2); // ✅ OK
expect({ a: 1, b: 2 }).to.include({ a: 1 }); // ✅ OK.not.include(value)
.not.include() は、配列やオブジェクトが指定された値を含んでいないか を確認します。
expect([1, 2, 3]).to.not.include(4); // ✅ OK
expect({ a: 1 }).to.not.include({ b: 2 }); // ✅ OK.have.property(propertyName)
.have.property(propertyName) は、オブジェクトが指定したプロパティを持っているか を確認します。
expect({ a: 1, b: 2 }).to.have.property('a'); // ✅ OK
expect({ a: 1 }).to.have.property('b'); // ❌ エラー(プロパティ 'b' がない)例外処理で使われるMatcher
例外処理で使われるMatcherは下記です。
.throw()
.throw() は、指定した関数が例外を投げるかどうか を確認するために使います。特定のエラーが発生することを期待する場合に使用します。
const func = () => { throw new Error('Something went wrong!'); };
expect(func).to.throw(); // ✅ OK
expect(func).to.throw('Something went wrong!'); // ✅ OKエラーメッセージだけでなく、特定のエラータイプが投げられるかも確認可能です。
const func = () => { throw new TypeError('Invalid type'); };
expect(func).to.throw(TypeError); // ✅ OK特定のエラーインスタンスが投げられるかも確認も確認可能です。
const func = () => { throw new TypeError('Invalid type'); };
expect(func).to.throw(new TypeError('Invalid type')); // ✅ OKその他のMatcher
その他のMatcherについて紹介します。
.match(regex)
正規表現にマッチするかを確認するためのMatcherです。
expect("hello world").to.match(/^hello/); // "hello" で始まる
expect("test123").to.match(/\d+$/); // 数字で終わる.satisfy(fn)
任意の関数の条件を満たすかどうかをテストできるMatcherです。
expect(10).to.satisfy(num => num % 2 === 0); // 偶数かどうか
expect("chai").to.satisfy(str => str.length === 4); // 長さが4文字か.oneOf(array)
値が特定の配列内のいずれかの要素であるかを確認できるMatcherです。
expect(5).to.be.oneOf([1, 2, 3, 5, 7]); // 5は配列に含まれる
expect("banana").to.be.oneOf(["apple", "banana", "cherry"]); // "banana" が含まれるまとめ
Chai は JavaScript のテストフレームワークで、Assert・Expect・Should の3つのスタイルを提供する柔軟なアサーションライブラリです。
Chaiを使いながらテストコードを作成し、より品質の高いソフトウェア開発を実現してみてください!
