第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で例外
  • [ ] <<演算子が正しく動作
  • ex01

  • [ ] Formの生成時にグレード検証
  • [ ] beSigned()が正しく動作
  • [ ] Bureaucrat::signForm()でメッセージ出力
  • [ ] <<演算子が正しく動作
  • ex02

  • [ ] AFormが抽象クラス
  • [ ] 3つの具象Formクラス
  • [ ] execute()で署名チェック
  • [ ] execute()でグレードチェック
  • [ ] Bureaucrat::executeForm()でメッセージ出力
  • ex03

  • [ ] Internが3種類のFormを作成
  • [ ] 未知のフォーム名でNULL返却
  • [ ] メモリリークなし
  • ---

    まとめ

    CPP Module 05のテストでは以下を確認:

  • グレード検証: 1-150の範囲チェック
  • 例外伝播: try-catchの正しい動作
  • Form操作: 署名と実行のフロー
  • ファクトリ: Internによる正しいForm生成