はじめに
ユニットテストを実装していると、何気ない感じで当たり前のようにMoqとかのツールを使っていますが、どんな使い方が正解なのかという意識をあまりしていませんでした。
改めて、ユニットテストについて学ぶ機会があり、その際にMicrosoft Documentにこんな記載を見つけました。
テストに関して、"モック" という用語は残念ながら誤用されることがしばしばあります。
重要
この用語を正確に理解することが重要です。 スタブを "モック" と名付けた場合、他の開発者はあなたの意図を誤って解釈してしまいます。
モックとスタブは違う。たまに聞く言葉なのですが、何が具体的に違うのか、なかなかすっきりしません。
とりあえず同サイトではこんな説明の仕方をしています。
モックとスタブに関する重要な留意点として、モックとスタブはよく似ていますが、モック オブジェクトに対してはアサートを行うのに対し、スタブに対してはアサートを行いません。
どういうことなのか。これについて自分なりに理解した範囲ですが記事にさせていただきます。
テストダブル(double) -- mock, spy, stub, dummy, fake
余談ですが、ダブルと聞いてHunterxHunterでカストロが念で自分の分身を作った場面を思い出します。
実際のところ、このテストで使用するダブルというのは、映画などの「スタントダブル」(Stunt double)に由来するそうです。
スタントマンといえば、ビルから飛び降りたり格闘シーンといった危険な場面に使用される代役のことですね。つまり、本物のように見せた代理人ということになります。
この概念をコーディングに応用したのが「テストダブル」ということになります。
この「テストダブル」には5つのバリエーションがあり、それがmock, spy, stub, dummy, fakeです。
テストツールでも、mockという呼び方をするものもいれば、spyと呼んでいるものもあります。
実際のところは、これはモックとスタブの2つのタイプに分類することができます。
モック(Mock)とスタブ(Stub)の違い
モックは、依存関係をエミュレートし、さらにそのメソッドの結果(副作用)を調べるために使用します。
スタブは、依存関係をエミュレートしてデータを取得するために使用します。
ここがポイントですが、英語にすると分かりやすいかもしれません。
- モックは、Emulate + Outcoming + Examine
- スタブは、Emulate + Incoming
このOutcoming, Incomingを送信・受信と翻訳して解釈している例もありますが、自分にはあまりイメージしにくかったです。
むしろ重要な違いは、このExamineがあるかどうかというところです。
さきほどのMicrosoft Documentの引用を思い出してください。こう書いてあったはずです。
モック オブジェクトに対してはアサートを行うのに対し、スタブに対してはアサートを行いません
モックとして扱う例
[Fact]
public void Send_Email_Test(){
// arrange
var mock = new Mock<IEmailService>();
var sut = new SenderClass(mock.Object);
// act
sut.SendEmail("mail@example.com");
// assertion
mock.Verify(x => x.SendingEmail("mail@example.com"), Times.Once);
}
この場合、mockはMockツールで生成されたモックとなり、モックなのでsut1でSendEmailメソッドが呼び出された時に、IEmailServiceのモックでSendingEmailメソッドが呼ばれているか検証しています。
つまり、モックは、エミュレートして、実際のその副作用を検証するという役割を果たしています。
スタブとして扱う例
[Fact]
public void Create_Report_Test(){
// arrange
var stub = new Mock<IBookRepositoty>();
stub.Setup(x => x.GetBookCount()).Returns(100);
var sut = new GetClass(stub.Object);
// act
Report report = sut.CreateReport();
// assertion
Assert.Equql(100, report.GetBookBookCount());
}
この場合、stubはMockツールで生成されたスタブとなり、スタブなのでsut1でCreateReportメソッドが呼び出された時の、戻り値を提供しています。
スタブは値を返すだけであり、検証されることはありません。
SQRSとの関係
ここで突然のようにSQRSを取り上げます。
というのも、『Unit Testing Principles, Practices, and Patterns』(Vladimir Khorikov, 2020, MANNING) という書籍に出会ったのですが、これが本当に分かりやすく、「なるほどー」と声を出してしまいました。もしご関係者がいらっしゃったら日本語訳を出してほしいです。
以下の図をご覧ください。書籍からの引用です2

CQRS(もしくはCQS)について詳細な説明は省きますが、クエリをコマンドとクエリに分ける考え方です。
コマンドは、CRUDのうち、Create, Update, Deleteといったデータに対し副作用(side effects)を与えます。
クエリはSelectなので、副作用はなく値を返します。
これをモックとスタブに当てはめると、モックはコマンドに対応し、スタブはクエリと対応するということです。
この表現を読んだとき、これまでのもやもやがすっきりしました。
モックは、結果(副作用)を与えるので、その作用についての検証が求められるわけです。
それに対しスタブは値を返すだけなので、検証対象とはならないということです。
どんな風に理解するのが分かりやすいかは人それぞれかと思いますが、自分はこれが一番すっきりする考えでしたので、記事としてご紹介させていただきました。