すぎしーのXRと3DCG

主にXR, Unity, 3DCG系の記事を投稿していきます。

【Moq】Moqはメソッドチェーンでまるごとモック化できるよ

メソッドチェーンのモック化

IFoo -> IBar -> IBaz のようにinterfaceのプロパティが連結されている場合、 以下の2つのモック化は同等になります。

            // setup
            var fooMock = new Mock<IFoo>();
            var barMock = new Mock<IBar>();
            var bazMock = new Mock<IBaz>();

            // when
            fooMock.Setup(m => m.Bar).Returns(barMock.Object);
            barMock.Setup(m => m.Baz).Returns(bazMock.Object);
            bazMock.Setup(m => m.Message).Returns("Hello Redundant");

            // then
            IFoo target = fooMock.Object;
            Assert.That(target.Bar.Baz.Message, Is.EqualTo("Hello Redundant"));
            Assert.That(target.Bar.Baz, Is.Not.Null);

            // setup
            var fooMock = new Mock<IFoo>();

            // when
            fooMock.Setup(m => m.Bar.Baz.Message).Returns("Hello Chain");

            // then
            IFoo target = fooMock.Object;
            Assert.That(target.Bar.Baz.Message, Is.EqualTo("Hello Chain"));

上部(以後Aパターン)のようにわざわざ、IFoo, IBar, IBazのMockを作る必要はなく、

下部(以後Bパターン)の fooMock.Setup(m => m.Bar.Baz.Message).Returns("Hello Chain"); のようにIFooからメソッドチェーンでまとめてモック化してくれるという話でした。

何が起こっているの?

どうやらAパターンの宣言でBパターンと同等になるようです。

後で検証コードを記載しますが、
target.BarMock<IBar>.Object, target.Bar.BazMock<IBaz>.Objectインスタンスが割り当てられていました。

Setupで指定しない場合はdefault

以下の用にSetupを実施しない場合はdefault、つまりnullを返します。

            // setup
            var fooMock = new Mock<IFoo>();

            // when
            // fooMock.Setup(m => m.Bar.Baz.Message).Returns("Hello Chain");

            // then
            IFoo target = fooMock.Object;
            Assert.That(() => target.Bar.Baz.Message, Throws.TypeOf<NullReferenceException>());

検証コード

  • NUnitで検証
  • IFoo, IBar, IBaz のinterfaceも定義

github.com

雑感

Moqを使ったことない同僚の方に、自分のコードを参考にMoqを使ってもらったら、
上記のようにメソッドチェーンでまるごとモック化する使い方をされていて発覚しました。

Moq使いだして2年以上、全然気づきませんでした。。。 先入観って怖いですね(笑)。

それでは~