Moq - 设置何处扩展?

时间:2016-09-19 13:37:39

标签: c# .net linq unit-testing moq

如何在对象上设置linq扩展名?我的情况DbSet。这是我的代码:

this.workflowStateSet
   .Setup(m => m.Where(It.IsAny<Expression<Func<Model.WorkflowState, int, bool>>>()))
   .Returns(new List<Model.WorkflowState>().AsQueryable());

然而,它给了我例外不是很熟悉的例外:

  

System.NotSupportedException:Expression引用一个方法   不属于模拟对象:m => m.Where<WorkflowState>

我会感激任何提示。

3 个答案:

答案 0 :(得分:2)

使用存储库模式为数据检索添加一个抽象层。然后可以嘲笑这种抽象。

例如,如果您尝试检索stateId等于1的所有工作流程,那么而不是调用类似这样的内容

var result = DbSet.WorkflowState.Where(w => w.stateId == 1);

将此代码移动到另一个类中,然后创建方法签名的接口。

public interface IWorkflowStateSetRepository{

     IQueryable<Model.WorkflowState> GetAllWorkflows(int state);
}

实施

public class WorkflowStateSetRepository : IWorkflowStateSetRepository{

    public IQueryable<Model.WorkflowState> GetAllWorkflows(int state){
        return DbSet.WorkflowState .Where(w => w.stateId == state);
    }
}

在调用代码中获取IWorkflowStateSetRepository的实例(可能来自您的IoC容器)并调用GetAllWorkflows()方法。这将为您提供与以前相同的结果,但您现在可以在测试中模拟界面并设置对该方法的调用。

this.MockedIWorkflowStateSetRepository.Setup(m => m.GetAllWorkflows(It.IsAny<int>()))
        .Returns(new List<Model.WorkflowState>().AsQueryable());

这段代码更易于维护,并且(通过适当命名的变量和方法)也可以更好地传达意图。

此处更详细地讨论了存储库模式;

http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

答案 1 :(得分:2)

此扩展方法将有助于模拟DbSet

public static class MockDbSetExtensions {
    public static Mock<DbSet<T>> AsDbSetMock<T>(this IEnumerable<T> list) where T : class {
        IQueryable<T> queryableList = list.AsQueryable();
        Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>();
        dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
        dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());
        return dbSetMock;
    }
}

你可以像这样使用它。

//Arrange
var data = new List<Model.WorkflowState>();
//you would populate your list as needed.
//convert it to a mock DbSet that uses the list as its datasource
var workflowStateSet = data.AsDbSetMock(); 
var dbSet = workflowStateSet.Object;

//Act
var items = dbSet.Where("Your expression here");

//Assert
//....

答案 2 :(得分:1)

您是否正在尝试模拟真实的DbSet实例?因为这将无法正常工作,因为错误消息试图解释你。要模拟类型,它必须是接口或具有虚拟成员(抽象成员也是虚拟成员)。

您可以尝试模拟IDbSet或创建自定义DbSet类,例如类似下面的类

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

public class DbSetMock<T> : DbSet<T>, IDbSet<T>
    where T : class
{
    private readonly ICollection<T> _contentCollection;

    public DbSetMock(IList<T> contentCollection = null)
    {
        _contentCollection = new Collection<T>(contentCollection ?? new List<T>());
        AddedEntities = new List<T>();
        RemovedEntities = new List<T>();
        AttachedEntities = new List<T>();
    }

    public void OverrideContentCollection(IEnumerable<T> newData)
    {
        _contentCollection.Clear();
        _contentCollection.AddRange(newData);
    }

    public IList<T> AddedEntities { get; private set; }

    public IList<T> AttachedEntities { get; private set; }

    public override ObservableCollection<T> Local
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    public IList<T> RemovedEntities { get; private set; }

    public Type ElementType
    {
        get
        {
            return typeof(T);
        }
    }

    public Expression Expression
    {
        get
        {
            return _contentCollection.AsQueryable().Expression;
        }
    }

    public IQueryProvider Provider
    {
        get
        {
            return _contentCollection.AsQueryable().Provider;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _contentCollection.GetEnumerator();
    }

    public override T Add(T entity)
    {
        AddedEntities.Add(entity);
        _contentCollection.Add(entity);
        return entity;
    }

    public override T Attach(T entity)
    {
        AttachedEntities.Add(entity);

        var matchingEntity = _contentCollection.SingleOrDefault(x => x.Id == entity.Id);
        if (matchingEntity != null)
        {
            _contentCollection.Remove(matchingEntity);
        }

        _contentCollection.Add(entity);

        return entity;
    }

    public override TDerivedEntity Create<TDerivedEntity>()
    {
        throw new NotImplementedException();
    }

    public override T Create()
    {
        throw new NotImplementedException();
    }

    public override T Find(params object[] keyValues)
    {
        throw new NotImplementedException();
    }

    public override T Remove(T entity)
    {
        RemovedEntities.Add(entity);
        _contentCollection.Remove(entity);
        return entity;
    }
}

您可以使用构造函数参数来设置将由db set检索的内容。

希望这有帮助。