EmotionTechテックブログ

株式会社エモーションテックのProduct Teamのメンバーが、日々の取り組みや技術的なことを発信していくブログです。

googletest-rust クレートを使ってみた

はじめに

こんにちは、バックエンドエンジニアのおおたわらです。

アドベントカレンダー 2 日目でテストコードの振り返りという記事も公開されましたが、弊社では単体テストをしっかりと書きながらプロダクト開発を進めています。

そんな中でテストをもっと書きやすくしたい!という思いから、Rust の googletest-rust クレートをプロダクト開発で使ってみたので、所感を紹介します。

この記事はエモーションテック Advent Calendar 2024の11日目の記事です。

googletest-rust とは

もともと Google 社が C++ 向けのテストライブラリ GoogleTest を公開しており、その Rust 版が googletest-rust です。

GoogleTest のコアはマッチャーと呼ばれる概念です。マッチャーとはテストの検査対象の値のどの側面を検証するかを表したものです。

googletest-rust には以下のような機能があります(大元の GoogleTest の機能を全て備えているわけではないようです)。

  • 豊富なビルトインマッチャー:Docs.rs の googletest::matchers - Rust から一覧を確認できますが、例えば基本的なものだと長さを検証する lenVec のようなコンテナに含まれる要素を順序問わず検証する unorderered_elements_are! などがあります。
  • 独自のマッチャー:Matcher トレイトを実装することで可能です。
  • アサーションマクロ:assert_that!expect_that!verify_that! の 3 つがあります。これらはアサーションが失敗した場合にテストを中止するかの挙動において違いがあります。

使ってみた所感

matches_pattern が便利!

開発の中で構造体や enum の値に関して何らかの検証をしたいことは多くあります。こういった場合には matches_pattern が便利です。

例えば構造体の検証は、以下のように各フィールドにマッチャーを指定して書くことができます。

#[derive(Debug)]
struct Survey {
    id: String,
    title: String,
    url: Url,
    questions: Vec<Question>,
}

#[derive(Debug)]
struct Url {
    subdomain: String,
    path: String,
}

#[derive(Debug)]
struct Question {
    id: String,
    text: String,
    required: bool,
}

#[gtest]
fn test() {
    let survey = ...

    verify_that!(
        survey,
        matches_pattern!(Survey {
            id: eq("id"),
            title: eq("title"),
            url: matches_pattern!(Url {
                subdomain: eq("hoge"),
                path: eq("fuga"),
            }),
            questions: len(eq(2)),
        })
    );
}

サンプルコードからわかる通り、入れ子で検証が可能です。また、すべてのフィールドを指定する必要がないので、検証したい箇所に絞ってアサーションを書くことができます。

ちなみに、pat という簡潔なエイリアスもあり、こちらのほうがコードは読みやすくなるので個人的にはよく使っています。

コンテナに対するマッチャーが豊富

コンテナ(Vec や配列)に対するマッチャーがいくつかあるのも嬉しい点です。

例えば順序も含めて検証する elements_are、順序は無視する unordered_elements_are があります。

他には要素を含むかどうかの検証に使える contains などもあります。

これらのマッチャーの引数もまたマッチャーなので、検証したい面にフォーカスしたテストを書くことができます。

    verify_that!(
        survey,
        matches_pattern!(Survey {
            questions: elements_are![
                matches_pattern!(Question {
                    text: char_count(gt(10)),
                }),
                matches_pattern!(Question {
                    text: starts_with("abc"),
                }),
            ],
        })
    );

マッチャーの組み合わせの自由度が高い

マッチャーとして抽象化されていることで、マッチャー同士を組み合わせて自由に検証したいことを確認できるように感じました。

例えば、allany によってマッチャーを連ねることで、1 つのアサーションでいろいろな角度からの検証を書くことができます。

    verify_that!(
        survey,
        matches_pattern!(Survey {
            title: all!(starts_with("hoge"), ends_with("fuga")),
        })
    );

他のテストクレートとも共存可能

rstesttokio::test のような他のテストクレートと一緒に使うこともできて安心です!

詳細は公式 GitHubこちらに記載がありますが、記載順に注意が必要だったり、非同期のテスト時に and_log_failure によるテスト失敗のレポートがうまく動かなかったりといったことがあるようです。

おわりに

googletest-rust を使ってみた所感をまとめてみました。使い始めたのは今年の夏ですが、自分が開発しているリポジトリでは欠かせないクレートになってきています。これからもお世話になると思います。

エモーションテックでは顧客体験、従業員体験の改善をサポートし、世の中の体験を変えるプロダクトを開発しています。ご興味のある方、Rust を使って開発したい方はぜひ採用ページからご応募をお願いいたします。

hrmos.co