neputa note

MoqでExpressionを引数に取るメソッドの備忘録

初稿:

更新:

- 3 min read -

img of MoqでExpressionを引数に取るメソッドの備忘録

背景

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

環境

  • 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 Tutorial => Getting started with moq

Moq Github

Quickstart · devlooped/moq Wiki

本題

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

code
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; }
}

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

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

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

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

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

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

code
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の中に条件を書く。

code
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);

以上で解決。

参考サイト

目次