第3章:テストとデバッグ
はじめに
本章では、CPP Module 05のテストとデバッグ方法を解説します。
---
1. Bureaucratのテスト
1.1 グレード範囲テスト
void testGradeRange() {
std::cout << "=== Grade Range Test ===" << std::endl;
// 有効な範囲
try {
Bureaucrat b1("Min", 1);
std::cout << "Grade 1: OK - " << b1 << std::endl;
} catch (const std::exception& e) {
std::cerr << "Grade 1: FAIL - " << e.what() << std::endl;
}
try {
Bureaucrat b2("Max", 150);
std::cout << "Grade 150: OK - " << b2 << std::endl;
} catch (const std::exception& e) {
std::cerr << "Grade 150: FAIL - " << e.what() << std::endl;
}
// 無効な範囲
try {
Bureaucrat b3("TooHigh", 0);
std::cerr << "Grade 0: FAIL - should throw" << std::endl;
} catch (const Bureaucrat::GradeTooHighException& e) {
std::cout << "Grade 0: OK - " << e.what() << std::endl;
}
try {
Bureaucrat b4("TooLow", 151);
std::cerr << "Grade 151: FAIL - should throw" << std::endl;
} catch (const Bureaucrat::GradeTooLowException& e) {
std::cout << "Grade 151: OK - " << e.what() << std::endl;
}
}
1.2 インクリメント/デクリメントテスト
void testIncrementDecrement() {
std::cout << "=== Increment/Decrement Test ===" << std::endl;
Bureaucrat bob("Bob", 75);
std::cout << "Initial: " << bob << std::endl;
bob.incrementGrade(); // 74
std::cout << "After increment: " << bob << std::endl;
bob.decrementGrade(); // 75
std::cout << "After decrement: " << bob << std::endl;
// 境界テスト
Bureaucrat top("Top", 1);
try {
top.incrementGrade();
std::cerr << "FAIL: should throw at grade 1" << std::endl;
} catch (const std::exception& e) {
std::cout << "OK: " << e.what() << std::endl;
}
Bureaucrat bottom("Bottom", 150);
try {
bottom.decrementGrade();
std::cerr << "FAIL: should throw at grade 150" << std::endl;
} catch (const std::exception& e) {
std::cout << "OK: " << e.what() << std::endl;
}
}
---
2. Formのテスト
2.1 署名テスト
void testFormSigning() {
std::cout << "=== Form Signing Test ===" << std::endl;
Form form("TestForm", 50, 25);
std::cout << form << std::endl;
Bureaucrat lowGrade("LowGrade", 100);
Bureaucrat highGrade("HighGrade", 30);
// 低いグレードでは署名できない
lowGrade.signForm(form);
std::cout << "After low grade attempt: " << form << std::endl;
// 高いグレードで署名
highGrade.signForm(form);
std::cout << "After high grade attempt: " << form << std::endl;
}
2.2 Form例外テスト
void testFormExceptions() {
std::cout << "=== Form Exceptions Test ===" << std::endl;
// 無効なグレード
try {
Form invalid1("Invalid", 0, 50);
std::cerr << "FAIL: should throw" << std::endl;
} catch (const std::exception& e) {
std::cout << "OK: " << e.what() << std::endl;
}
try {
Form invalid2("Invalid", 50, 151);
std::cerr << "FAIL: should throw" << std::endl;
} catch (const std::exception& e) {
std::cout << "OK: " << e.what() << std::endl;
}
}
---
3. AForm/具象Formのテスト
3.1 execute前の署名確認
void testExecuteWithoutSign() {
std::cout << "=== Execute Without Sign Test ===" << std::endl;
ShrubberyCreationForm form("test");
Bureaucrat boss("Boss", 1);
// 署名なしで実行 → 例外
try {
form.execute(boss);
std::cerr << "FAIL: should throw FormNotSignedException" << std::endl;
} catch (const AForm::FormNotSignedException& e) {
std::cout << "OK: " << e.what() << std::endl;
}
}
3.2 各Formの実行テスト
void testFormExecution() {
std::cout << "=== Form Execution Test ===" << std::endl;
Bureaucrat boss("Boss", 1);
// ShrubberyCreationForm
ShrubberyCreationForm shrub("garden");
boss.signForm(shrub);
boss.executeForm(shrub);
// garden_shrubberyファイルが作成される
// RobotomyRequestForm
RobotomyRequestForm robot("Bender");
boss.signForm(robot);
boss.executeForm(robot);
// PresidentialPardonForm
PresidentialPardonForm pardon("Criminal");
boss.signForm(pardon);
boss.executeForm(pardon);
}
3.3 グレード不足テスト
void testGradeRequirements() {
std::cout << "=== Grade Requirements Test ===" << std::endl;
// PresidentialPardonForm requires grade 5 to execute
PresidentialPardonForm pardon("Target");
Bureaucrat signer("Signer", 20); // Can sign (needs 25)
Bureaucrat executor("Executor", 10); // Cannot execute (needs 5)
signer.signForm(pardon);
try {
pardon.execute(executor);
std::cerr << "FAIL: should throw GradeTooLowException" << std::endl;
} catch (const AForm::GradeTooLowException& e) {
std::cout << "OK: " << e.what() << std::endl;
}
}
---
4. Internのテスト
4.1 Form作成テスト
void testInternFormCreation() {
std::cout << "=== Intern Form Creation Test ===" << std::endl;
Intern intern;
AForm* form1 = intern.makeForm("shrubbery creation", "target1");
AForm* form2 = intern.makeForm("robotomy request", "target2");
AForm* form3 = intern.makeForm("presidential pardon", "target3");
AForm* form4 = intern.makeForm("nonexistent form", "target4");
std::cout << "Shrubbery: " << (form1 ? "Created" : "NULL") << std::endl;
std::cout << "Robotomy: " << (form2 ? "Created" : "NULL") << std::endl;
std::cout << "Pardon: " << (form3 ? "Created" : "NULL") << std::endl;
std::cout << "Nonexistent: " << (form4 ? "Created" : "NULL") << std::endl;
delete form1;
delete form2;
delete form3;
}
4.2 完全な処理フロー
void testCompleteFlow() {
std::cout << "=== Complete Flow Test ===" << std::endl;
Intern intern;
Bureaucrat boss("Boss", 1);
AForm* form = intern.makeForm("presidential pardon", "Bob");
if (form) {
std::cout << *form << std::endl;
boss.signForm(*form);
std::cout << *form << std::endl;
boss.executeForm(*form);
delete form;
}
}
---
5. よくある間違い
5.1 例外クラスのwhat()
// 間違い: throw()指定がない
const char* what() const {
return "Error";
}
// 正しい: throw()を指定(C++98互換)
const char* what() const throw() {
return "Error";
}
5.2 例外を値でキャッチ
// 間違い: スライシングが発生
catch (std::exception e) {
std::cerr << e.what() << std::endl;
}
// 正しい: 参照でキャッチ
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
5.3 constメンバの代入
// 間違い
Bureaucrat& Bureaucrat::operator=(const Bureaucrat& other) {
_name = other._name; // _nameはconst
_grade = other._grade;
return *this;
}
// 正しい: constメンバは代入できない
Bureaucrat& Bureaucrat::operator=(const Bureaucrat& other) {
if (this != &other) {
// _name は const なので代入しない
_grade = other._grade;
}
return *this;
}
5.4 Form署名状態のチェック漏れ
// 間違い: 署名チェックなし
void AForm::execute(const Bureaucrat& executor) const {
if (executor.getGrade() > _gradeToExecute) {
throw GradeTooLowException();
}
executeAction();
}
// 正しい: 署名と実行グレードの両方をチェック
void AForm::execute(const Bureaucrat& executor) const {
if (!_signed) {
throw FormNotSignedException();
}
if (executor.getGrade() > _gradeToExecute) {
throw GradeTooLowException();
}
executeAction();
}
5.5 InternでのNULL返却
// 間違い: 未知のフォーム名で例外
AForm* Intern::makeForm(const std::string& name, const std::string& target) {
// ...
throw std::runtime_error("Unknown form"); // 適切ではない
}
// 正しい: NULLを返してエラーメッセージ出力
AForm* Intern::makeForm(const std::string& name, const std::string& target) {
// ...
std::cerr << "Unknown form: " << name << std::endl;
return NULL;
}
---
6. 提出前チェックリスト
ex00
- [ ] Bureaucratの生成時にグレード検証
- [ ] GradeTooHighException/GradeTooLowException
- [ ] incrementGrade/decrementGradeで例外
- [ ]
<<演算子が正しく動作 - [ ] Formの生成時にグレード検証
- [ ] beSigned()が正しく動作
- [ ] Bureaucrat::signForm()でメッセージ出力
- [ ]
<<演算子が正しく動作 - [ ] AFormが抽象クラス
- [ ] 3つの具象Formクラス
- [ ] execute()で署名チェック
- [ ] execute()でグレードチェック
- [ ] Bureaucrat::executeForm()でメッセージ出力
- [ ] Internが3種類のFormを作成
- [ ] 未知のフォーム名でNULL返却
- [ ] メモリリークなし
- グレード検証: 1-150の範囲チェック
- 例外伝播: try-catchの正しい動作
- Form操作: 署名と実行のフロー
- ファクトリ: Internによる正しいForm生成
ex01
ex02
ex03
---
まとめ
CPP Module 05のテストでは以下を確認: