×

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

目次 [隠す]

.NETとMoqのロゴ

引数に条件式(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 Github

moq / moq4

本題

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

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

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

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

このメソッドを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(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(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

Comments

0 件のコメント :