「TAURI」と「Rust」の「テスト」機能を試してみよう

2024年3月12日(火)
大西 武 (オオニシ タケシ)
第11回の今回は「Rust」で値が正しいか「テスト」する「assert_eq!」マクロなどを解説します。また「TAURI」でもプログラムが正しく動作するかの「テスト」についても解説します。

TAURIにおけるRustのテスト機能

TAURIのRust部分をテストする際は「src-tauri」フォルダをカレントディレクトリにすればテストできますが、フロントエンドの「HTML5+JavaScript+CSS」はテストできません。つまりバックエンドのRustプロジェクトのみテストできるので、TAURIコマンド関数などだけテストします。

せっかくなのでTAURIで単純な計算ゲームを作ります。ゲーム画面の下方に表示された数字の二乗をどんどん答えていくクイズです。正解しなければ問題の数字は変わりません。正解してどんどん数字を大きくしていきましょう。

TAURIプロジェクトの作成

第2回のTAURIの設定が済んでいる前提で話を進めていきます。適当なフォルダをカレントディレクトリにして、次のコマンドを実行して「tauri_test」などという名前でプロジェクトを新規作成します。それから作成した「tauri_test」フォルダをカレントディレクトリにします。追加で必要なクレートはありません。

・TAURIプロジェクトの作成コマンド
$ cargo create-tauri-app tauri_test

テンプレートのコードを書き換える

まず、TAURIのテンプレートを次の「index.html」「main.js」「main.rs」ファイルのサンプルコードのように書き換えます。できるだけテンプレートと同じままに書き換えています。「index.html」ファイルはstyleタグ、.rowセレクタタグ、pタグを消してh2タグを追加し、formタグ内を変更しました。「main.js」ファイルは「greet」関数内の1行を書き換えただけです。「main.rs」ファイルはTAURIコマンド「greet」関数を全て書き換えました。

次にHTMLで数値の二乗を答えるインターフェースを作ります。[Answer]ボタンを押すとJavaScriptで答えが合っているかバックエンドにメッセージを送ります。答えが合っていたらRustで答えの数の文字列を返し、間違っていたら問題のままの数の文字列を返します。これで完成です。

「$ cargo-tauri dev」コマンドでアプリを実行すると、図3のような計算クイズの画面が出ます。今回はCSSをいじっていませんが、スタイルシートで見た目を自由にデザインできるので、気に入らなかったら改造してみると良いでしょう。

図3:計算クイズの画面

・「src」→「index.html」ファイルのサンプルコード
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="styles.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tauri App</title>
    <script type="module" src="/main.js" defer></script>
  </head>

  <body>
    <div class="container">
      <h1>下の数字の二乗を答えよう!</h1>

      <form class="row" id="greet-form">
        <input id="greet-input" placeholder="正解で下の数値が入力値に" />
        <button type="submit">Answer</button>
      </form>

      <h2 id="greet-msg">2</h2>
    </div>
  </body>
</html>

【サンプルコードの解説】
「h1」タグにゲームの説明を書きます。
「form」タグの中に文字入力欄の「input」タグと、解答ボタンの「button」タグを配置します。
「h2」タグに問題の数字を書きます。

・「src」→「main.js」ファイルのサンプルコード
const { invoke } = window.__TAURI__.tauri;

let greetInputEl;
let greetMsgEl;

async function greet() {
  //答えた値と問題の値をバックエンドにメッセージを送る
  greetMsgEl.textContent = await invoke("greet", { answer: ~~greetInputEl.value, question: ~~greetMsgEl.innerText });
}

window.addEventListener("DOMContentLoaded", () => {
  greetInputEl = document.querySelector("#greet-input");
  greetMsgEl = document.querySelector("#greet-msg");
  document.querySelector("#greet-form").addEventListener("submit", (e) => {
    e.preventDefault();
    greet();
  });
});

【サンプルコードの解説】
「greet」関数でバックエンドのRustに"greet"メッセージを答えた整数値(「answer」キー)と問題の整数値(「question」キー)とともに渡し、返ってきた文字列を「#greet-msg」セレクタのtextContentに代入します。

・「src-tauri」→「src」→「main.rs」ファイルのサンプルコード
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

//計算が正しいか調べるTAURIコマンド関数
#[tauri::command]
fn greet(answer: u64,question: u64) -> String {
    if answer == question*question {
      answer.to_string()
    } else {
      question.to_string()
    }
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

【サンプルコードの解説】
TAURIコマンド「greet」関数で答えた数値(「answer」引数)が問題の数値(「question」引数)の二乗に等しければanswer引数を文字列にして返し、等しくなければquestion引数を文字列にして返します。

TAURIのRust部分のテストコードを書く

TAURIのRust部分をテストするためには、必ず「src-tauri」フォルダをカレントディレクトリにします。「src-tauri」がRustのプロジェクト名に当たるので、このフォルダをRustプロジェクトのカレントディレクトリにしてから次のテストコードをコーディングします。前述のようにあえてライブラリプロジェクトにしなくても、次のサンプルコードのようにテストはできます。

「src-tauri」→「src」→「main.rs」のテストコード
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

//テストモジュール
#[cfg(test)]
mod tests {
  //外部の定義を全て使えるように
  use super::*;
  //テスト関数
  #[test]
  fn it_works() {
    //第1引数と第2引数が等しいかテスト
    assert_eq!(greet(4,2), "4");
  }
}

#[tauri::command]
(後略)

【サンプルコードの解説】
「tests」モジュールをテストできるようにします。
「use super::*」でtestsモジュール外の全ての定義をtestsモジュール内でも使えるようにします。
テストする「it_works」関数でTAURIコマンドgreet関数に2の二乗を解答した4と問題の2を渡し、その戻り値の文字列が"4"文字列に等しいかテストします。

次のコマンドでテストを実行してみると、図4のような文がターミナルに表示されます。「tests::it_works」が「test result: ok.」でテストの結果はOKでした。以降のコメントは「1 passed;」で1個テストを通過しました、「0 failed;」で0個テストが失敗しました、「0 ignored;」で0個テストが無視されました、「0 measured;」で0個テストでパフォーマンスを調べました(これはnightly版のRustのみの機能です)、「0 filtered out;」で0個テストでフィルターがかけられました、となります。

・Rustのテストを実行
$ cargo test

図4:テスト結果の画面

おわりに

今回は、Rustのテスト機能を解説しました。テスト機能はプログラムをデバッグするときに、プログラムのバグがどうしても解決できない場合に威力を発揮するでしょう。

また、今回はTAURIのテストにおいて「assert_eq!」マクロしか解説しませんでしたが、ぜひみなさんご自身で同様に「assert_ne!」「assert!」「panic!」マクロもテストしてみてください。

著者
大西 武 (オオニシ タケシ)
1975年香川県生まれ。大阪大学経済学部経営学科中退。プログラミング入門書など30冊以上を商業出版。Microsoftで大賞やNTTドコモでグランプリなど20回以上全国区のコンテストに入賞。オリジナルの間違い探し「3Dクイズ」が全国放送のTVで約10回出題。

連載バックナンバー

開発言語技術解説
第14回

「TAURI」で気象庁の「CSVデータ」を解析する

2024/5/1
第14回の今回は気象庁のWebサイトから指定した地域の1年間の気象データをダウンロードして「TAURI」で解析していきます。
開発言語技術解説
第13回

「TAURI」で「簡易RSSリーダー」を開発してみよう

2024/4/16
第13回の今回は「TAURI」で「RSSフィード」を読み込んでWebページに一覧表示し、リンクのページを開くための新規ウィンドウを作成するところまでを解説します。
開発言語技術解説
第12回

「TAURI」でExcelのデータを読み書きしてWebページに表示してみよう

2024/4/2
第12回の今回は「TAURI」で「Rust」の「umya-spreadsheet」クレートを使って「Excel」の「xlsx」ファイルを読み書きし、Webページに表示するところまでを解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています