如何将EF Mock设置代码写成可重用的通用锅炉? [英] How to write this EF Mock setup code as a reusable Generic Boilerplate?

查看:168
本文介绍了如何将EF Mock设置代码写成可重用的通用锅炉?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用moq,ef 6和xunit。我发现自己一遍又一遍地写这个代码,也许我可以把它编成一个通用的方法,但是有麻烦。

  public static void CreateSalesMock(List< Sale> sales,Mock< DatabaseContext> dbContextMock)
{
var data = sales.AsQueryable();

var mockSet = new Mock< DbSet< Sale>>();
mockSet.As< IQueryable< Sale>>()
.Setup(x => x.Provider)
.Returns(data.Provider);
mockSet.As< IQueryable< Sale>>()
.Setup(x => x.Expression)
.Returns(data.Expression);
mockSet.As< IQueryable< Sale>>()
.Setup(x => x.ElementType)
.Returns(data.ElementType);
mockSet.As< IQueryable< Sale>>()
.Setup(x => x.GetEnumerator())
.Returns(data.GetEnumerator());

dbContextMock.Setup(x => x.Sales).Returns(mockSet.Object);
}

现在我的数据库中有很多其他表,所以如果我可以写一个方法这将收录该数据的列表,并设置它,所以我可以模拟查询通过它将是巨大的。

  public static void CreateMockSet< T,TA,TB>(T dataList,TA model,
Func< TB> lambda,Mock< DatabaseContext> dbContextMock)
其中T:List< T&
其中TA:Mock< DbSet>>
{
var data = dataList.AsQueryable();

model.As< IQueryable< T>()
.Setup(x => x.Provider)
.Returns(data.Provider);
model.As< IQueryable< T>()
.Setup(x => x.Expression)
.Returns(data.Expression);
model.As< IQueryable< T>()
.Setup(x => x.ElementType)
.Returns(data.ElementType);
model.As< IQueryable< T>()
.Setup(x => x.GetEnumerator())
.Returns(data.GetEnumerator());

dbContextMock.Setup(x => lambda);
}

到目前为止,我有这个,但我不知道这是否会工作。我被卡住了lambda部分(例如 x => x.Sales ),所以我甚至不能测试它。

解决方案

Tim Larson已经为他的博客

  public static class DbSetMocking 
{
private static Mock< DbSet< T>> CreateMockSet< T>(IQueryable< T>数据)
其中T:class
{
var queryableData = data.AsQueryable();
var mockSet = new Mock< DbSet>>();
mockSet.As< IQueryable< T>()。Setup(m => m.Provider)
.Returns(queryableData.Provider);
mockSet.As< IQueryable< T>()。Setup(m => m.Expression)
.Returns(queryableData.Expression);
mockSet.As< IQueryable< T>()。Setup(m => m.ElementType)
.Returns(queryableData.ElementType);
mockSet.As< IQueryable< T>()。Setup(m => m.GetEnumerator())
.Returns(queryableData.GetEnumerator());
return mockSet;
}

public static IReturnsResult< TContext>返回数组(< TEntity),TContext>(
此IReturns< TContext,DbSet< TEntity>>设置,
TEntity []实体)
其中TEntity:class
其中TContext:DbContext
{
Mock< DbSet< TEntity>> mockSet;
return ReturnsDbSet(setup,entities,out mockSet);
}

public static IReturnsResult< TContext>返回DbSet< TEntity,TContext>(
此IReturns< TContext,DbSet< TEntity>>设置,
IQueryable< TEntity>实体)
其中TEntity:class
其中TContext:DbContext
{

Mock< DbSet< TEntity>> mockSet;
return ReturnsDbSet(setup,entities,out mockSet);
}

public static IReturnsResult< TContext> ()
其中TEntity:class
其中TContext:DbContext(
这个IReturns< TContext,DbSet< TEntity>>设置,
IEnumerable< TEntity>
{
Mock< DbSet< TEntity>> mockSet;
return ReturnsDbSet(setup,entities,out mockSet);
}

public static IReturnsResult< TContext>返回DbSet< TEntity,TContext>(
此IReturns< TContext,DbSet< TEntity>>设置,
TEntity []实体,出Mock< DbSet< TEntity>> mockSet)
其中TEntity :class
其中TContext:DbContext
{
mockSet = CreateMockSet(entities.AsQueryable());
return setup.Returns(mockSet.Object);
}

public static IReturnsResult< TContext>返回DbSet< TEntity,TContext>(
此IReturns< TContext,DbSet< TEntity>>设置,
IQueryable< TEntity>实体,出Mock< DbSet< TEntity>> mockSet)
其中TEntity:class
其中TContext:DbContext
{

mockSet = CreateMockSet(entities);
return setup.Returns(mockSet.Object);
}

public static IReturnsResult< TContext>返回DbSet< TEntity,TContext>(
此IReturns< TContext,DbSet< TEntity>>设置,
IEnumerable< TEntity>实体,出Mock< DbSet< TEntity>> mockSet)
TEntity:class
其中TContext:DbContext
{
mockSet = CreateMockSet(entities.AsQueryable());
return setup.Returns(mockSet.Object);
}

}

然后在UT中使用它如下所示:

  var context = new Mock< DatabaseContext>(); 
context.setup(x => x.Sales).ReturnsDbSet(new List< Sale>(){put here the items ..});

修改



我更新了代码。现在还有3个重载,允许在 DbSet< T> 属性:

  [TestMethod] 
public void TestMethod1()
{
var sales = new List< Sale>
{
new Sale(){id = 1},
new Sale(){id = 6},
new Sale(){id = 5},
new Sale(){id = 4},
new Sale(){id = 3},
new Sale(){id = 2}
};
var fakeContest = new Mock< SalesContext>();
Mock< DbSet< Sale>> fakeSet;
fakeContest.Setup(context => context.Sales).ReturnsDbSet(sales,out fakeSet);

var itemsToRemove = sales.Where(sale => sale.id%2 == 0);


fakeContest.Object.Sales.RemoveRange(itemsToRemove);


fakeSet.Verify(set => set.RemoveRange(itemsToRemove));

}


I am using moq, ef 6 and xunit. I find myself writing this code over and over and thought maybe I could make it into a generic method but having some trouble.

public static void CreateSalesMock(List<Sale> sales, Mock<DatabaseContext> dbContextMock)
{
    var data = sales.AsQueryable();

    var mockSet = new Mock<DbSet<Sale>>();
    mockSet.As<IQueryable<Sale>>()
           .Setup(x => x.Provider)
           .Returns(data.Provider);
    mockSet.As<IQueryable<Sale>>()
           .Setup(x => x.Expression)
           .Returns(data.Expression);
    mockSet.As<IQueryable<Sale>>()
           .Setup(x => x.ElementType)
           .Returns(data.ElementType);
    mockSet.As<IQueryable<Sale>>()
           .Setup(x => x.GetEnumerator())
           .Returns(data.GetEnumerator());

    dbContextMock.Setup(x => x.Sales).Returns(mockSet.Object);
}

Now I have many other tables in my database so if I could write one method that would take in a list of that data and set it up so I can mock query through it would be great.

public static void CreateMockSet<T, TA, TB>(T dataList, TA model, 
    Func<TB> lambda, Mock<DatabaseContext> dbContextMock) 
     where T : List<T>
     where TA: Mock<DbSet<TA>>
{
    var data = dataList.AsQueryable();

    model.As<IQueryable<T>>()
         .Setup(x => x.Provider)
         .Returns(data.Provider);
    model.As<IQueryable<T>>()
         .Setup(x => x.Expression)
         .Returns(data.Expression);
    model.As<IQueryable<T>>()
         .Setup(x => x.ElementType)
         .Returns(data.ElementType);
    model.As<IQueryable<T>>()
         .Setup(x => x.GetEnumerator())
         .Returns(data.GetEnumerator());

    dbContextMock.Setup(x => lambda);
}

So far I have that but I am not sure if that will work or not. I am stuck passing in the "lambda" part(x => x.Sales for example) so I can't even test it.

解决方案

Tim Larson already offered a great solution for this boilerplate code in his blog:

public static class DbSetMocking
{
    private static Mock<DbSet<T>> CreateMockSet<T>(IQueryable<T> data)
            where T : class
    {
        var queryableData = data.AsQueryable();
        var mockSet = new Mock<DbSet<T>>();
        mockSet.As<IQueryable<T>>().Setup(m => m.Provider)
                .Returns(queryableData.Provider);
        mockSet.As<IQueryable<T>>().Setup(m => m.Expression)
                .Returns(queryableData.Expression);
        mockSet.As<IQueryable<T>>().Setup(m => m.ElementType)
                .Returns(queryableData.ElementType);
        mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator())
                .Returns(queryableData.GetEnumerator());
        return mockSet;
    }

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
            this IReturns<TContext, DbSet<TEntity>> setup,
            TEntity[] entities)
        where TEntity : class
        where TContext : DbContext
    {
        Mock<DbSet<TEntity>> mockSet;
        return ReturnsDbSet(setup, entities, out mockSet);
    }

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
            this IReturns<TContext, DbSet<TEntity>> setup,
            IQueryable<TEntity> entities)
        where TEntity : class
        where TContext : DbContext
    {

        Mock<DbSet<TEntity>> mockSet;
        return ReturnsDbSet(setup, entities, out mockSet);
    }

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
            this IReturns<TContext, DbSet<TEntity>> setup,
            IEnumerable<TEntity> entities)
        where TEntity : class
        where TContext : DbContext
    {
        Mock<DbSet<TEntity>> mockSet;
        return ReturnsDbSet(setup, entities, out mockSet);
    }

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
    this IReturns<TContext, DbSet<TEntity>> setup,
    TEntity[] entities, out Mock<DbSet<TEntity>> mockSet)
        where TEntity : class
        where TContext : DbContext
    {
        mockSet = CreateMockSet(entities.AsQueryable());
        return setup.Returns(mockSet.Object);
    }

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
            this IReturns<TContext, DbSet<TEntity>> setup,
            IQueryable<TEntity> entities, out Mock<DbSet<TEntity>> mockSet)
        where TEntity : class
        where TContext : DbContext
    {

        mockSet = CreateMockSet(entities);
        return setup.Returns(mockSet.Object);
    }

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
    this IReturns<TContext, DbSet<TEntity>> setup,
    IEnumerable<TEntity> entities, out Mock<DbSet<TEntity>> mockSet)
        where TEntity : class
        where TContext : DbContext
    {
        mockSet = CreateMockSet(entities.AsQueryable());
        return setup.Returns(mockSet.Object);
    }

}

Then in the UT you use it as the following:

var context = new Mock<DatabaseContext>();
context.setup(x => x.Sales).ReturnsDbSet(new List<Sale>(){put here the items..});

Edit

I updated the code. Now there are 3 more overloads which allows verification on DbSet<T> property:

    [TestMethod]
    public void TestMethod1()
    {
        var sales = new List<Sale>
        {
            new Sale() {id = 1},
            new Sale() {id = 6},
            new Sale() {id = 5},
            new Sale() {id = 4},
            new Sale() {id = 3},
            new Sale() {id = 2}
        };
        var fakeContest = new Mock<SalesContext>();
        Mock<DbSet<Sale>> fakeSet;
        fakeContest.Setup(context => context.Sales).ReturnsDbSet(sales, out fakeSet);

        var itemsToRemove = sales.Where(sale => sale.id%2 == 0);


        fakeContest.Object.Sales.RemoveRange(itemsToRemove);


        fakeSet.Verify(set => set.RemoveRange(itemsToRemove));

    }

这篇关于如何将EF Mock设置代码写成可重用的通用锅炉?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆