Simple Mocking in Rust

Simple Mocking in Rust

582c6ad10cb28c4f6cf49447316a784c?s=128

Takanori Ishibashi

September 05, 2018
Tweet

Transcript

  1. Simple Mocking in Rust Rust入門者の集い #5 @11Takanori

  2. About Me • @11Takanori • 仕事だと、主にKotlin、たまにClojure、Dart、ごくたまにRust、 Elixirを使ってる

  3. mockを使いたい理由 マイクロサービスA,B,Cがあるとする 1. マイクロサービスA,B,Cを組み合わせたE2Eを書く 2. マイクロサービスA単独のE2Eを書く ← 別のマイクロサービスをmockしたい 3. 2が通るようになるまで単体テストと実装を繰り返す(クリーンアーキテクチャのレイ

    ヤごと) ← 別のレイヤをmockしたい 4. マイクロサービスBに対しても同様 5. マイクロサービスCに対しても同様 6. マイクロサービスA,B,Cを組み合わせたE2Eが通る こんな感じで開発していると、mockしたくなる
  4. 単体テストのmock 今回はライブラリを使用せず、ユーザーにメールを 送る処理を素朴にmockする

  5. #[derive(Clone)] struct Email { from: String, to: String, msg: String,

    } struct User { address: String, } trait EmailSender { fn send_mail(&self, msg: &Email) -> Result<String, String>; //mock対象 } 実装
  6. // テスト対象 fn publish_news(msg: &str, sender: &EmailSender, users: &[User]) ->

    u32 { let mut count = 0; let mut mail = Email { from: "from@example.com".to_string(), to: "".to_string(), msg: msg.to_string(), }; for user in users { mail.to = user.address.to_string(); match sender.send_mail(&mail) { // send_mail(&mail)をmockしたい Ok(_) => count += 1, Err(msg) => println!("Failed to send mail: {}", msg), } } count } 実装
  7. struct MockEmailSender { // send_mailの引数の格納先 sent_mails: RefCell<Vec<Email>>, // RefCellは不変値を可変借用できる }

    impl MockEmailSender { fn new() -> Self { MockEmailSender { sent_mails: RefCell::new(Vec::new()), } } } impl EmailSender for MockEmailSender { fn send_mail(&self, mail: &Email) -> Result<String, String> { // RefCellに格納しないと&mut selfを使うことになり、シグニチャーが一致しなくなる self.sent_mails.borrow_mut().push(mail.clone()); Ok("200 OK".to_string()) } } テスト
  8. #[test] fn send_mail_to_users() { let user1 = User { address:

    "user1@example.com".to_string(), }; let user2 = User { address: "user2@example.com".to_string(), }; let send = publish_news("hello world", &MockEmailSender::new(), &[user1, user2]); assert_eq!(send, 2) // 意図した戻り値になっていることをチェック } テスト
  9. #[test] fn send_correct_mail() { let user = User { address:

    "to@example.com".to_string(), }; let sender = MockEmailSender::new(); publish_news("hello world", &sender, &[user]); let mails = sender.sent_mails.borrow(); // Ref<Vec<Email>> assert_eq!(mails.len(), 1); // send_mailを呼んだ回数をチェック assert_eq!(mails[0].from, "from@example.com"); // 引数のチェック assert_eq!(mails[0].to, "to@example.com"); // 引数のチェック assert_eq!(mails[0].msg, "hello world"); // 引数のチェック } テスト
  10. Rustのmockライブラリ • Mockers (nightly) • Double • Mock_Derive (nightly) •

    Galvanic-mock (nightly) • Pseudo • Mock-it • Simulacrum デファクトスタンダード的なものは特になさそう。