nikkie-ftnextの日記

イベントレポートや読書メモを発信

『Rustの練習帳』1章 assert_cmdを使ったテストの実装についてドキュメントで調べたことメモ

はじめに

ジュエルを買うなら! ASOBI STORE! nikkieです。

『Rustの練習帳』1章を写経まで終えました1
書籍の範囲から逸脱してドキュメントを読みに行ったメモです。
テスト関係のところを調べています。

目次

テストでtarget/debug下の実行ファイルを実行する

Cargo.tomldev-dependenciesに追加。
https://crates.io/crates/assert_cmd

assert_cmd aims to simplify the process for doing integration testing of CLIs, including:

  • Finding your crate's binary to test
  • Assert on the result of your program's run.

Command::cargo_bin
https://docs.rs/assert_cmd/latest/assert_cmd/cmd/struct.Command.html#method.cargo_bin

Create a Command to run a specific binary of the current crate.

target/debug下の実行ファイルを実行してくれます。
環境変数PATHの兼ね合いで設定がいるのですが(※書籍参照)、クレートを使うだけで済むなんてこれは便利です

cargo_bin()の返り値はResult<Self, CargoError>
これはテストなのでCargoErrorならそこで落としてよく、unwrap()という理解です。
https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap

unwrap()すると(Selfなので)assert_cmdCommandが返っているのだと思っています。

trueコマンド

テストコード
https://github.com/kyclark/command-line-rust/blob/10d983f68e84b9c94057da6ecf555bc419e11999/01_hello/tests/cli.rs#L13-L17

cmd.assert().success();

Commandのassertメソッド
https://docs.rs/assert_cmd/latest/assert_cmd/cmd/struct.Command.html#method.assert

Run a Command and make assertions on the Output.

ここでコマンドが実行されるんですね!
返すオブジェクトはAssert
そのsuccess()メソッドは https://docs.rs/assert_cmd/latest/assert_cmd/assert/struct.Assert.html#method.success

Ensure the command succeeded.

trueコマンドが成功すると表明しているわけですね。
(満たされなければテストが落ちて気付ける)

これ以上は実装を見てみないとですが、終了ステータスが0になることを見ているのかな?

falseコマンド

実装に使ったのはstd::process::abort()
https://doc.rust-lang.org/std/process/fn.abort.html

Terminates the process in an abnormal fashion.

なお、trueの実装で使っていたのはstd::process::exit()でした2
https://doc.rust-lang.org/std/process/fn.exit.html

Terminates the current process with the specified exit code.

テストコードですが、
https://github.com/kyclark/command-line-rust/blob/10d983f68e84b9c94057da6ecf555bc419e11999/01_hello/tests/cli.rs#L19-L23

cmd.assert().failure();

failure()メソッド
https://docs.rs/assert_cmd/latest/assert_cmd/assert/struct.Assert.html#method.failure

Ensure the command failed.

helloコマンド、出力のテスト

https://github.com/kyclark/command-line-rust/blob/30e2bca7ee38e3e907cb1a4063109fefb77077ee/01_hello/tests/cli.rs#L3-L7

cmd.assert().success().stdout("Hello, world!\n");

Assertsuccess()メソッドはSelfすなわちAssertを返します。
stdout()メソッドは
https://docs.rs/assert_cmd/latest/assert_cmd/assert/struct.Assert.html#method.stdout

Ensure the command wrote the expected data to stdout.

引数で渡した文字列が標準出力に書かれたことを保証しています。
末尾の改行込みで「Hello, world」と出力されたことを検証するわけですね3

終わりに

assert_cmdを使った実装について、写経中に見たドキュメントのメモでした。

  • cargo_bin()unwrap()でコマンドを得る
  • assert()でコマンドを実行
  • success()failure()でコマンドの成功/失敗を検証
  • success().stdout("...")は成功していて、かつ、標準出力も渡した文字列通りかを検証

書籍のペースで進んでもよいと思いますが、私の傾向として手広く知っておいたほうが楽しんで学んでいけるのでまとめています。

いまはよく分かっていないことが一つ。
let mutってなぜこう書くんでしょうか?

// https://docs.rs/assert_cmd/latest/assert_cmd/cmd/struct.Command.html#method.cargo_bin
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))
    .unwrap();

letが変数への束縛で、mutが可変にするという理解なのですが、let cmd =だとなんでいけないんでしょう?
変数に束縛されている値自体が変わる??

写経の状態


  1. 前回はcargo newを取り上げています
  2. Pythonでいうとsys.exit()ですかね https://docs.python.org/ja/3/library/sys.html#sys.exit
  3. pytestのcapsysっぽいなと思いました https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html#accessing-captured-output-from-a-test-function