ユニットテスト(test() / expect() / group())
『Dart』の dart:test パッケージは、ユニットテストを記述・実行するための標準的なテストフレームワークです。test() 関数でテストケースを定義し、expect() でアサーションを記述します。dart test コマンドでテストを実行できるため、品質を保ちながら開発を進められます。
構文
// パッケージのインポートです
import 'package:test/test.dart';
// test():1つのテストケースを定義します
test('テスト名', () {
// テスト本体を記述します
expect(実際の値, マッチャー);
});
// group():関連するテストをまとめてグループ化します
group('グループ名', () {
setUp(() {
// 各テストの前に実行される初期化処理です
});
tearDown(() {
// 各テストの後に実行されるクリーンアップ処理です
});
test('テスト名', () {
expect(実際の値, マッチャー);
});
});
// よく使うマッチャーです
expect(value, equals(期待値)); // 等値比較
expect(value, isTrue); // true であることを確認
expect(value, isFalse); // false であることを確認
expect(value, isNull); // null であることを確認
expect(value, isNotNull); // null でないことを確認
expect(value, isA<型名>()); // 型チェック
expect(value, contains('文字列')); // 文字列・コレクションに要素が含まれることを確認
expect(value, hasLength(n)); // 長さの確認
expect(value, greaterThan(n)); // n より大きいことを確認
expect(value, lessThan(n)); // n より小さいことを確認
expect(value, throwsA(isA<例外型>())); // 例外がスローされることを確認
expect(() => fn(), throwsException); // 何らかの例外がスローされることを確認
構文一覧
| 関数/マッチャー | 概要 |
|---|---|
test(name, body) | 1つのテストケースを定義します。name にテスト内容を表す文字列を、body にテスト処理を記述します。 |
group(name, body) | 関連するテストをグループにまとめます。グループ名はテスト結果の表示時にテスト名と連結されます。 |
setUp(fn) | 各テストの実行前に呼び出されるセットアップ関数を登録します。テストごとに状態を初期化する際に使います。 |
tearDown(fn) | 各テストの実行後に呼び出されるクリーンアップ関数を登録します。リソースの解放などに使います。 |
setUpAll(fn) | グループ内の全テストを実行する前に1回だけ呼び出されます。重いリソースの初期化に使います。 |
tearDownAll(fn) | グループ内の全テストを実行した後に1回だけ呼び出されます。 |
expect(actual, matcher) | 実際の値がマッチャーの条件を満たすかを検証します。条件を満たさない場合はテストが失敗します。 |
equals(expected) | 値が expected と等しいことを検証するマッチャーです。 |
isTrue / isFalse | 値が true または false であることを検証するマッチャーです。 |
isNull / isNotNull | 値が null であるか、または null でないことを検証するマッチャーです。 |
isA<T>() | 値が型 T のインスタンスであることを検証するマッチャーです。 |
contains(value) | 文字列やコレクションが指定した要素を含むことを検証するマッチャーです。 |
hasLength(n) | 文字列やコレクションの長さが n であることを検証するマッチャーです。 |
greaterThan(n) / lessThan(n) | 値が n より大きい、または小さいことを検証するマッチャーです。 |
throwsA(matcher) | 関数が指定したマッチャーに合致する例外をスローすることを検証するマッチャーです。 |
throwsException | 関数が何らかの Exception をスローすることを検証するマッチャーです。 |
throwsArgumentError | 関数が ArgumentError をスローすることを検証するマッチャーです。 |
サンプルコード
jjk_sorcerer_test.dart
// jjk_sorcerer_test.dart — dart:test の基本的な使い方のサンプルです
// 呪術廻戦の術師クラスをテスト対象として、各種テスト機能を確認します
import 'package:test/test.dart';
// -----------------------------------------------
// テスト対象のクラス定義
// -----------------------------------------------
// 術師の等級を表す列挙型です
enum SorcererGrade { special, first, second, third, fourth }
// 術師クラスです
class Sorcerer {
final String name;
final SorcererGrade grade;
final String technique;
int cursedEnergy; // 呪力(変化する値)
Sorcerer({
required this.name,
required this.grade,
required this.technique,
required this.cursedEnergy,
});
// 呪術を発動します。呪力を消費して発動結果を返します
String activate(int cost) {
if (cost <= 0) {
throw ArgumentError('発動コストは 1 以上でなければなりません');
}
if (cursedEnergy < cost) {
throw StateError('呪力が不足しています(必要: $cost, 残: $cursedEnergy)');
}
cursedEnergy -= cost;
return '${name} が ${technique} を発動しました(残呪力: $cursedEnergy)';
}
// 呪力を回復します
void recover(int amount) {
if (amount <= 0) {
throw ArgumentError('回復量は 1 以上でなければなりません');
}
cursedEnergy += amount;
}
// 特級かどうかを返します
bool get isSpecialGrade => grade == SorcererGrade.special;
// 術師の情報を文字列で返します
@override
String toString() => '$name(${grade.name}): $technique';
}
// 術師チームクラスです
class SorcererTeam {
final String teamName;
final List<Sorcerer> members;
SorcererTeam({required this.teamName, required this.members});
// チームの合計呪力を返します
int get totalCursedEnergy =>
members.fold(0, (sum, s) => sum + s.cursedEnergy);
// 特級術師のリストを返します
List<Sorcerer> get specialGradeMembers =>
members.where((s) => s.isSpecialGrade).toList();
// 名前でメンバーを検索します。見つからない場合は null を返します
Sorcerer? findByName(String name) {
try {
return members.firstWhere((s) => s.name == name);
} catch (_) {
return null;
}
}
}
// -----------------------------------------------
// テストコード
// -----------------------------------------------
void main() {
// -----------------------------------------------
// Sorcerer クラスのテスト
// -----------------------------------------------
group('Sorcerer クラス', () {
// 各テスト前に術師インスタンスを初期化します
late Sorcerer gojo;
late Sorcerer itadori;
late Sorcerer nobara;
setUp(() {
// setUp はテストごとに呼ばれるため、常に新鮮な状態でテストできます
gojo = Sorcerer(name: '五条悟', grade: SorcererGrade.special, technique: '無下限呪術', cursedEnergy: 9999);
itadori = Sorcerer(name: '虎杖悠仁', grade: SorcererGrade.first, technique: '発勁', cursedEnergy: 500);
nobara = Sorcerer(name: '釘崎野薔薇', grade: SorcererGrade.third, technique: '芻霊呪法', cursedEnergy: 300);
});
// --- equals マッチャーのテストです ---
test('name プロパティが正しく設定されること', () {
expect(gojo.name, equals('五条悟'));
expect(itadori.name, equals('虎杖悠仁'));
expect(nobara.name, equals('釘崎野薔薇'));
});
// --- isTrue / isFalse マッチャーのテストです ---
test('isSpecialGrade が正しく判定されること', () {
expect(gojo.isSpecialGrade, isTrue); // 五条悟は特級術師
expect(itadori.isSpecialGrade, isFalse); // 虎杖悠仁は特級でない
});
// --- 状態変化のテストです ---
test('activate() が呪力を正しく消費すること', () {
var result = itadori.activate(100);
// 消費後の残呪力を確認します
expect(itadori.cursedEnergy, equals(400));
// 戻り値の文字列に術師名と術式名が含まれることを確認します
expect(result, contains('虎杖悠仁'));
expect(result, contains('発勁'));
});
test('recover() が呪力を正しく回復すること', () {
nobara.recover(200);
expect(nobara.cursedEnergy, equals(500));
});
// --- greaterThan / lessThan マッチャーのテストです ---
test('五条悟の呪力は虎杖悠仁より大きいこと', () {
expect(gojo.cursedEnergy, greaterThan(itadori.cursedEnergy));
expect(nobara.cursedEnergy, lessThan(itadori.cursedEnergy));
});
// --- 例外テストです ---
group('activate() の例外テスト', () {
test('コストが 0 以下のとき ArgumentError をスローすること', () {
// throwsA と isA でスローされる例外の型を確認します
expect(
() => itadori.activate(0),
throwsA(isA<ArgumentError>()),
);
});
test('呪力が不足しているとき StateError をスローすること', () {
expect(
() => nobara.activate(9999), // 呪力 300 なのに 9999 を要求
throwsA(isA<StateError>()),
);
});
test('recover() のコストが 0 以下のとき ArgumentError をスローすること', () {
// throwsArgumentError はよく使う省略形マッチャーです
expect(() => gojo.recover(-1), throwsArgumentError);
});
});
// --- toString のテストです ---
test('toString() が正しいフォーマットを返すこと', () {
var str = gojo.toString();
// isA で型を確認します
expect(str, isA<String>());
expect(str, contains('五条悟'));
expect(str, contains('無下限呪術'));
});
});
// -----------------------------------------------
// SorcererTeam クラスのテスト
// -----------------------------------------------
group('SorcererTeam クラス', () {
late SorcererTeam team;
// setUpAll はグループ全体で1回だけ実行されます
setUpAll(() {
print('SorcererTeam テストの準備を開始します');
});
setUp(() {
// 各テストの前にチームを初期化します
team = SorcererTeam(
teamName: '東京校選抜チーム',
members: [
Sorcerer(name: '五条悟', grade: SorcererGrade.special, technique: '無下限呪術', cursedEnergy: 9999),
Sorcerer(name: '虎杖悠仁', grade: SorcererGrade.first, technique: '発勁', cursedEnergy: 500),
Sorcerer(name: '釘崎野薔薇', grade: SorcererGrade.third, technique: '芻霊呪法', cursedEnergy: 300),
Sorcerer(name: '伏黒恵', grade: SorcererGrade.first, technique: '十種影法術', cursedEnergy: 600),
],
);
});
tearDownAll(() {
print('SorcererTeam テストが完了しました');
});
// --- hasLength マッチャーのテストです ---
test('チームのメンバー数が 4 人であること', () {
expect(team.members, hasLength(4));
});
// --- isNotNull / isNull マッチャーのテストです ---
test('findByName() で存在するメンバーが見つかること', () {
var found = team.findByName('伏黒恵');
expect(found, isNotNull);
expect(found!.technique, equals('十種影法術'));
});
test('findByName() で存在しないメンバーは null を返すこと', () {
var notFound = team.findByName('夏油傑');
expect(notFound, isNull);
});
// --- コレクション系マッチャーのテストです ---
test('specialGradeMembers に五条悟だけが含まれること', () {
var specials = team.specialGradeMembers;
expect(specials, hasLength(1));
expect(specials.first.name, equals('五条悟'));
});
test('totalCursedEnergy が全メンバーの合計と一致すること', () {
// 9999 + 500 + 300 + 600 = 11399
expect(team.totalCursedEnergy, equals(11399));
});
});
}
dart test jjk_sorcerer_test.dart Building package executable... Built test:test. 00:02 +12: All tests passed!
概要
『Dart』のテストフレームワークは package:test/test.dart で提供されており、test() でテストケースを、group() で関連テストをまとめて定義します。setUp() と tearDown() はテストごとに実行される初期化・クリーンアップ処理で、setUpAll() と tearDownAll() はグループ全体で1回だけ実行されます。アサーションには expect() と各種マッチャー(equals、isTrue、isNull、isA<T>()、contains、throwsA 等)を組み合わせて記述します。テストは dart test ファイル名.dart で実行でき、全テストが通ると All tests passed! と表示されます。例外処理の実装については 例外処理(try / catch / throw) を、非同期テストについては async / await も参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。