MoqでExpressionを引数に取るメソッド をセットアップする メモ

2020/12/08

目次 [隠す]

背景

引数に条件式(Expression)を取るメソッドをMoqにSetupしようとした際に少しハマったのでメモ。

.NETとMoqのロゴ

環境

  • IDE:Visual Studio Community 2019 Ver.16.8.2
  • .NET Version:Core 3.1
  • 言語:C#
  • TestFramework:xUnit 2.4.1
  • Moq Version:4.15.2

Moqについて

Moqとは

Moqは. Net用の模擬ライブラリです。 ユニットテストを容易にするために、依存関係との対話をシミュレートして検証することができます。

Moq Github

moq / moq4

本題

例えばこのようなUserクラスがあります。

User class
internal class User
{
    public User(UserId userId, UserName userName, UserAge userAge)
    {
        Id = userId ?? throw new ArgumentNullException(nameof(userId));
        Name = userName ?? throw new ArgumentNullException(nameof(userName));
        Age = userAge ?? throw new ArgumentNullException(nameof(userAge));
    }

    public UserId Id { get; }
    public UserName Name { get; }
    public UserAge Age { get; }
}

そしてこのような条件式を引数に取るメソッドがあります。

IUserRepository interface
internal interface IUserRepository
{
    IEnumerable<User> FindWithCondition(Expression<Func<User, bool>> predicate);
}

このメソッドをMoqにSetupで追加するにはこのように書く。

Moq.Setup
var moq = new Mock<IUserRepository>();

moq.Setup(x => x.FindWithCondition(It.IsAny<Expression<Func<User, bool>>>()))
      .Returns(users);

ただこれだとIt.IsAny()を使用しているため、どのような条件でも返す値は同じとなります。

It.Is()で条件によって異なる値を返せないかやってみると、

Moq.Setup
moq.Setup(x => x.FindWithCondition(It.Is<Expression<Func<User, bool>>>(
    x => x.Age.Value >= 20 && x.Age.Value <= 29)))
    .Returns(users20);
moq.Setup(x => x.FindWithCondition(It.Is<Expression<Func<User, bool>>>(
    x => x.Age.Value >= 30 && x.Age.Value <= 39)))
    .Returns(users30);

インテリセンスも効くし普通に書けますが、「Ageの定義が含まれていない」と怒られます。

理由としては、Moqが式の等価の評価を実装していないから。

ではどうするかというと、Returnsの中に条件を書く。

Moq.Setup
moq.Setup(x => x.FindWithCondition(It.IsAny<Expression<Func<User, bool>>>()))
    .Returns((Expression<Func<User, bool>> predicate) =>
      users.AsQueryable().Where(predicate));

var userAppService = new UserApplicationService(moq.Object);

以上で解決。

参考サイト

Moq.Mock<T> - how to set up a method that takes an expression

VSCode・VisualStudio関連の記事

多階層の要素を一括削除したい【VSCode - Vim】 | neputa note

例えばこんなHTMLコードがあった場合、大外のdivを含む全コードを少ない手数で削除したい。Vimの場合matchit.vim をONにしていれば、Visualモードで開始タグを選択し、終了タグまで「

blog card

VSCodeでもStyleCopを使いたい【C#】 | neputa note

VisualStudioでC#を記述する際に「StyleCopAnalyzers(以降StyleCop)」というコード解析ツールを使用しています。チーム開発ではもちろん、個人開発においても整ったコーデ

blog card

editorconfig for VSCode でコーディングルールを 統一する【C#】 | neputa note

前回記事のStyleCopは、C#や.NETのコーディングスタイルを規定するため、警告を表示してくれるツールでした。今回のeditorconfigは、C#や.NETはもちろん、VSCodeのエディタ全

blog card

Unit Test が実行できなくなった 原因と対処 【 VisualStudio 2019 / xUnit 】 | neputa note

xUnitを使っています。Visual Studio 2019 で突然 UnitTest が実行できない状況に。原因と対処を備忘録としてノートする。Visual Studio 2019.NET

blog card