👻

Moq から NSubstitute へ移行する

2023/08/09に公開

Moq から NSubstitute へ移行する

X (Twitter) で知りました。

https://twitter.com/neuecc/status/1689180754214047744

.NET で有名なテスト用の Mock ライブラリ、Moq の 4.20.0 から SponsorLink というものが埋め込まれました。GitHub の Issue で盛り上がっています。Privacy issues with SponsorLink, starting from version 4.20

ざっと読んだ感じですが、スポンサーになってくれた人のメールアドレスでは warning が出ないようにするってことみたいです。その際、メールアドレス (ハッシュ化されているようです) が API サーバーに送信されていて、特にクローズドな開発では問題になるとかとかいろいろ言われています。

  • 気にしない
  • 4.18.4 からアップデートしない
  • 別のライブラリに移行する

の選択肢があります。せっかくなので、NSubstitute に乗り換えることにしました。

NSubstitute へ乗り換える

とりあえずインストール。

$ dotnet add package NSubstitute

元の Moq バージョンから。

var mock = new Mock<ICalculator>();

mock.Setup(x => x.Add(1, 2)).Returns(3);

ICalculator target = mock.Object;

Console.WriteLine(target.Add(1, 2)); // 3

NSubstitute に書き換えます。

var target = Substitute.For<ICalculator>();

target.Add(1, 2).Returns(3);

Console.WriteLine(target.Add(1, 2)); // 3

面倒は面倒ですけど...

ところで、Moq は Setup(...) メソッドの引数に式木を渡します。Moq はこの式木を解析してごにょごにょするわけです。NSubstitute は式木を渡していないように見えます。どうやってるんでしょう? 不思議です。

いろいろ

任意のパラメーター

Moq では It.IsAny<int>() などが用意されています。

mock.Setup(x => x.Add(It.IsAny<int>(), It.IsAny<tin>()).Returns(3);

NSubstitute では Arg.Any<int>() を使います。

target.Add(Arg.Any<int>(), Arg.Any<int>());

Arg.Is<int>(x => x < 0) のように条件を指定することもできます。詳しくは Argument matchers を読んでください。

Async の戻り値

非同期メソッドの戻り値は Moq では次のように書きます。

mock.Setup(x => x.FetchAsync()).ReturnsAsync("晴れ");

NSubstitute ではもっと簡単です。

target.FetchAsync().Returns("晴れ");

呼び出されたかの検証

呼び出されたを検証する場合、Moq では次のように書きます。

// 呼び出された
mock.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<string>()));

// 呼び出されなかった
mock.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<string>()), Times.Never);

NSubstitute では次のようになります。

// 呼び出された
target.ReceivedWithAnyArgs().Add(Arg.Any<int>(), Arg.Any<int>());

// 呼び出されなかった
target.DidNotReceiveWithAnyArgs().Add(Arg.Any<int>(), Arg.Any<int>());

詳しくは Checking received calls を読んでください。

そのほか

Docs and getting help

終わり

移行は面倒だけど、機能的に不足してるとかはなさそうです。とにかくめんどくさいけど...

実際に移行した Merge Request です。このプロジェクトではモックオブジェクトのファクトリ関数を作っていたので、それほど書き換えの量は多くありません。

余談

Moq の開発者が収入に苦労しなければこんなことにはならないんでしょうけどね。そういう意味だと、お世話になってるけどスポンサーしていない自分も悪いのです。でも、自分も金がない...

Discussion