Open16
rust のテストでモックを使う
https://github.com/serenity-rs/serenity これを使って discord bot を作っているが、ほとんどの動作が 外部依存なので、モックなしではテストが満足に書けない
impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) {
println!("{} is connected!", ready.user.name);
ctx.set_activity(Activity::playing("こんにちは"))
.await;
}
}
こういったものを mock する場合は Context を mock しないといけないが、これはライブラリの trait なので、素直に mock ができない。
こういう場合はどうやって書いたらいいのか
それっぽいのがあった
mock でこれを怒られたらもうどうすればいいのか
近い気はするけどイマイチ答えじゃない気がする
これも違う
外部lib だからて入れられなくて mock を使おうとしてるのにそれをさせてくれない
そんなあほな
外部の struct をラップした自前の struct を作って、それを mock する形で書いた
use serenity::{async_trait, model::gateway::Activity};
#[cfg_attr(test, mockall::automock)]
#[async_trait]
pub trait ActivityController {
async fn set_activity(&self, activity: Activity);
}
pub async fn set_help_message_to_activity(ctx: Box<dyn ActivityController + Send + Sync>) {
ctx.set_activity(Activity::playing("hoge"))
.await;
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_set_activity() {
let mut controller = MockActivityController::new();
controller.expect_set_activity().times(1).return_const(());
set_help_message_to_activity(Box::new(controller)).await
}
}
一旦この方針で進めてみる
python の mock.patch みたいな感じでさっとモックする使い方はできなさそう
(今のところ方法はわかってない)
mockall を入れる
Cargo.toml
[dependencies]
mockall = "^0.10.2"
interface となる trait を定義する
#[async_trait]
pub trait Printer {
async fn print(&self, &str);
}
interface を mock 化する
#[cfg_attr(test, mockall::automock)]
#[async_trait]
pub trait Printer {
async fn print(&self, &str);
}
trait を使う関数を定義(テストしたい関数)
pub async fn printer_print(printer: &Printer) {
printer.print("hoge")
.await;
}
テストを書く
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_printer_print() {
# `#[cfg_attr(test, mockall::automock)]` で定義すると `MockHoge` の形で呼べるようになる
let mut printer = MockPrinter::new();
# `expect_hoge` で `hoge` 関数を mock できる
printer.expect_print().times(1).return_const(());
printer_print(printer).await
}
}