Open16

rust のテストでモックを使う

tkttkt
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 ができない。
こういう場合はどうやって書いたらいいのか

tkttkt


mock でこれを怒られたらもうどうすればいいのか

tkttkt

外部lib だからて入れられなくて mock を使おうとしてるのにそれをさせてくれない

tkttkt

ライブラリを無理に使おうとせず imple Context for Mock すればいけたりするんだろうか

tkttkt
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
    }
}

一旦この方針で進めてみる

tkttkt

python の mock.patch みたいな感じでさっとモックする使い方はできなさそう
(今のところ方法はわかってない)

tkttkt

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
    }
}