2

I'm working on a unit test and I'd like to verify that a mock object received the proper predicate argument. However, I fail to make it work.

Here's a skeleton of the code:

public interface IRepository<T>
{
    Task<T> SingleOrDefaultAsync(Expression<Func<T, bool>> predicate);   
}

public class MyClass
{
    private readonly IRepository<Foo> _repository

    public MyClass(IRepository<Foo> repository)
    {
        _repository = repository;
    }

    public Task<bool> MyMethod(int id)
    {
        var foo = _repository.SingleOrDefaultAsync(x => x.Id == id);

        return foo != null;
    }
}

and in my test class I have the following method

public async Task MyTestMethod()
{
    var repository = Substitute.For<IRepository<Foo>>();
    repository.SingleOrDefaultAsync(x => x.Id == 123).Returns(Task.FromResult(new Foo()));

    var myClass = new MyClass(repository);

    var result = await myClass.MyMethod(123);

    result.Should().BeTrue();
}

But as mentioned above, this test fails. I could make it pass by using Arg.Any<Expression<Func<Foo, bool>>, but it doesn't feel right.

Anyone has a suggestion what I'm doing wrong?

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Peter Szekeli
  • 2,712
  • 3
  • 30
  • 44
  • 1
    In general anonymous functions can not be tested for structural equality, only reference equality. @Nkosi's answer shows one approach for working around this, and I've written up [some other options too](https://stackoverflow.com/a/5658873/906). – David Tchepak Mar 15 '20 at 21:25

1 Answers1

1

Capture the expression passed to the mock and use it in the Returns to verify the expected behavior.

For example

public async Task MyTestMethod() {
    //Arrange
    var id = 123;
    var model = new Foo() {
        Id = id;
    }

    var repository = Substitute.For<IRepository<Foo>>();

    repository.SingleOrDefaultAsync(Arg.Any<Expression<Func<Foo, bool>>())
        .Returns(args => {
            var expression = args.Arg<Expression<Func<Foo, bool>>(); //capture expression
            Foo result = expression.Compile()(model) ? model : null; //use to verify behavior
            Task.FromResult(result);
        });

    var myClass = new MyClass(repository);

    //Act
    var actual = await myClass.MyMethod(id);

    //Assert
    actual.Should().BeTrue();
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472