CMakeテストの成功/失敗をもっとスマートに!FAIL_REGULAR_EXPRESSIONと代替方法徹底解説


CMakeの「Properties: Tests」における「FAIL_REGULAR_EXPRESSION」プロパティは、テスト実行時に出力される標準出力(stdout)または標準エラー出力(stderr)に一致する正規表現を指定することで、テストを意図的に失敗させる機能を提供します。テストのステータス判定は、プログラムの終了コードだけでなく、出力内容にも基づいて行われます。

用途

「FAIL_REGULAR_EXPRESSION」プロパティは、以下の目的で活用できます。

  • テストカバレッジの向上
    テストケースが想定されるすべてのエラー条件を網羅していることを確認するために使用できます。
  • 特定の動作の検証
    テスト対象プログラムが特定のメッセージを出力することを確認する場合に使用できます。
  • 予期しない出力を検出
    テストが予期しないエラーメッセージや警告を出力した場合、テストを失敗させて問題箇所を特定することができます。

構文

set_tests_properties(test_name FAIL_REGULAR_EXPRESSION regular_expression1 regular_expression2 ...)
  • ...: 任意の数の正規表現を追加できる
  • regular_expression2: テスト失敗判定に使用する正規表現2
  • regular_expression1: テスト失敗判定に使用する正規表現1
  • test_name: 対象となるテストの名前

set_tests_properties(my_test FAIL_REGULAR_EXPRESSION "Error:.*" "Failed to allocate memory")

この例では、my_test という名前のテストが以下のいずれかの正規表現に一致する出力を出力した場合、失敗と判定されます。

  • "Failed to allocate memory": "Failed to allocate memory" というメッセージ
  • "Error:.*": "Error:" で始まる任意のエラーメッセージ
  • 複数の正規表現を指定する場合は、スペースで区切ります。
  • テストの終了コードが0であっても、出力内容に一致する正規表現があれば、テストは失敗と判定されます。
  • 正規表現は、Perl Compatible Regular Expressions (PCRE) の形式で記述する必要があります。
  • 「SKIP_REGULAR_EXPRESSION」プロパティを使用して、特定の出力が出力された場合にテストをスキップすることもできます。
  • 「PASS_REGULAR_EXPRESSION」プロパティと併用することで、テスト成功判定の条件を指定することもできます。


add_test(NAME my_test COMMAND my_program)
set_tests_properties(my_test FAIL_REGULAR_EXPRESSION "Error:.*")

例2:特定の動作を検証する

この例では、my_test という名前のテストが "Success: Operation completed" というメッセージを出力することを確認します。

add_test(NAME my_test COMMAND my_program)
set_tests_properties(my_test FAIL_REGULAR_EXPRESSION ".*Error|.*Failed")
set_tests_properties(my_test PASS_REGULAR_EXPRESSION "Success: Operation completed")

例3:テストカバレッジを向上させる

この例では、my_test という名前のテストが想定されるすべてのエラー条件を網羅していることを確認します。

add_test(NAME my_test COMMAND my_program)
set_tests_properties(my_test FAIL_REGULAR_EXPRESSION "Error:.*" "Failed to allocate memory" "Invalid input")
  • テストケースの設計と実装においては、テストカバレッジを意識することが重要です。すべてのエラー条件を網羅するテストケースを作成することで、プログラムの品質向上に貢献することができます。
  • 上記の例はあくまで基本的な使用方法を示したものです。実際の使用例では、テスト対象プログラムや目的に合わせて、適切な正規表現を記述する必要があります。


しかし、「FAIL_REGULAR_EXPRESSION」プロパティにはいくつかの注意点があり、状況によっては代替方法を検討する必要があります。

注意点

  • テスト結果の解釈が困難
    テストログに正規表現に一致するメッセージが出力された場合、テストが失敗した原因を特定しにくい場合があります。
  • テスト実行速度の低下
    正規表現による出力解析処理が追加されるため、テスト実行速度が低下する可能性があります。
  • 複雑な正規表現の記述が困難
    テスト対象プログラムが出力するメッセージ構造が複雑な場合、適切な正規表現を記述することが難しい場合があります。

代替方法

上記のような注意点がある場合、以下の代替方法を検討することができます。

カスタムアサーションを使用する

テスト対象プログラムの出力内容を直接チェックするカスタムアサーションを作成することで、「FAIL_REGULAR_EXPRESSION」プロパティよりも柔軟かつ効率的なテスト結果判定を実現できます。

add_test(NAME my_test COMMAND my_program)

test_check_output(my_test EXPECT "Success: Operation completed")

テスト対象プログラムを修正する

テスト対象プログラムを修正し、テストに必要な情報を出力形式として明確に出力することで、「FAIL_REGULAR_EXPRESSION」プロパティに頼らずにテスト結果を判定することができます。

テストフレームワークを利用する

Boost TestやGoogle Testなどのテストフレームワークは、豊富なアサーション機能を提供しており、「FAIL_REGULAR_EXPRESSION」プロパティよりも使いやすく、テスト結果の解釈も容易になります。

add_test(NAME my_test COMMAND my_program)

BOOST_TEST(my_test_success)
{
  BOOST_CHECK_EQUAL(my_program(), 0);
  BOOST_CHECK_EQUAL(std::string(stderr.rdbuf()), "");
}

テストログを解析するスクリプトを作成する

テスト実行後に生成されたログファイルを解析するスクリプトを作成することで、「FAIL_REGULAR_EXPRESSION」プロパティよりも柔軟なテスト結果判定を実現できます。

import re

def parse_log(log_file):
  with open(log_file, 'r') as f:
    for line in f:
      if re.search("Error:.*", line):
        return False
  return True

if not parse_log('my_test.log'):
  print('Test failed')