System.NotSupportedException : Unsupported expression: x => x

Viewed 19937

I'm currently trying to moq my Cafe Get method which will throw a ArgumentNullexception if the cafe ID is not found.

Error

System.NotSupportedException : Unsupported expression: x => x.Cafe Non-overridable members (here: Context.get_Cafe) may not be used in setup / verification expressions.

Is this occurring because moq is unable to handle one of the setup expressions?

Unit test

[Fact]
public async Task GetCafeByIdAsync_Should_Throw_ArgumentNullException()
{
    var cafe = new List<Cafe>()
    {
        new Cafe { Name = "Hanna", CafeId = 1},
        new Cafe { Name = "Bella", CafeId = 2 }
    }.AsQueryable();

    var mockSet = new Mock<DbSet<Cafe>>();
    mockSet.As<IQueryable<Cafe>>().Setup(m => m.Provider).Returns(cafe.Provider);
    mockSet.As<IQueryable<Cafe>>().Setup(m => m.Expression).Returns(cafe.Expression);
    mockSet.As<IQueryable<Cafe>>().Setup(m => m.ElementType).Returns(cafe.ElementType);
    mockSet.As<IQueryable<Cafe>>().Setup(m => m.GetEnumerator()).Returns(cafe.GetEnumerator());

    var mapper = new MapperConfiguration(cfg =>
    {
        cfg.AddProfile(new AutoMapperProfile());
    }).CreateMapper();

    var contextMock = new Mock<Context>();
    contextMock.Setup(x => x.Cafe).Returns(mockSet.Object); //failing here

    var cafeService = new CafeService(contextMock.Object, mapper);

    await Assert.ThrowsAsync<ArgumentNullException>(() => cafeService.Get(2));
}

SUT

public async Task<VersionResponse> Get(int cafeId)
{
    var cafe = await _context.Cafe.Where(w => w.CafeId == cafeId).ToResponse().FirstOrDefaultAsync();
    return new VersionResponse()
    {
        Data = cafe
    };
}
3 Answers

Moq relies on being able to create a proxy class that overrides properties. The Context.Cafe can't be overridden. Try declaring that property virtual.

public virtual IDbSet<Cafe> Cafe { get; set; }

Try setup mock on Set method instead

public virtual DbSet<TEntity> Set<TEntity>() where TEntity : class;

in your case

var contextMock = new Mock<Context>();
contextMock.Setup(x => x.Set<Cafe>()).Returns(mockSet.Object);

I Got a Better way For Integration Tests You can Use Transaction RollBack for this Type of Tests in this way you can do what ever you want in Db and then every single movement in Db are back to last state of Before Running Tests

if you using Ef you can add this class to your Test Project and Enjoy it

public abstract class PersistTest<T> : IDisposable where T : DbContext, new()
{
    protected T DBContext;
    private TransactionScope scope;
    protected PersistTest()
    {
        scope = new TransactionScope();
        DBContext = new T();
    }

    public void Dispose()
    {
        scope.Dispose();
        DBContext.Dispose();
    }
}

this class help you to write better integration tests,

your test class should inherited from this abstract class and pass your context class as a instance of DbContext

and one more thing about your Codd you should seperate your Logic and DataAccess from each other

just define a interface Called ICofeeRepository and take your methods in it Your Code Break the Single Responsibility principle

Your Service Should be Like

public class CoffeService
{
    //inject cafe repository ; _repo
    public async Task<VersionResponse> Get(int cafeid)
    {
        var cafe = _repo.FindById(cafeid);
        if (cafe == null)
            throw Exception("");
        //other wise .....
    }
}

and your Repository Should be Like

public interface ICafeRepository
{
    Cafe Get(int cafeid);
}
Related