Is there a way to generalize the DbSet.Find method using Moq?

I am currently using an extension method to fake DbSets as a general list:

    public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class
    {
        var queryable = sourceList.AsQueryable();
        var mockDbSet = new Mock<DbSet<T>>();
        mockDbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
        mockDbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
        mockDbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
        mockDbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
        mockDbSet.Setup(x => x.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);
        mockDbSet.Setup(x => x.Remove(It.IsAny<T>())).Returns<T>(x => { if (sourceList.Remove(x)) return x; else return null; } );

        return mockDbSet.Object;
    }

However, I cannot find a way to mock the Find method, which searches based on the primary key of the table. I could do this at a certain level for each table, because I can check the database, get the PK, and then just make fun of the Find method for this field. But then I can not use the general method.

I suppose I could also add to the partial classes that EF automatically generated to mark which field is PK with an attribute or something else. But we have more than 100 tables, and this makes code management difficult if you rely on people to manually maintain them.

EF6 - ?

+4
4

, , "" . if, . , , find , . -, .

        if (typeof(T) == typeof(MyFirstSet))
        {
            mockDbSet.Setup(x => x.Find(It.IsAny<object[]>())).Returns<object[]>(x => (sourceList as List<MyFirstSet>).FirstOrDefault(y => y.MyFirstSetKey == (Guid)x[0]) as T);
        }
        else if (typeof(T) == typeof(MySecondSet))
        {
            mockDbSet.Setup(x => x.Find(It.IsAny<object[]>())).Returns<object[]>(x => (sourceList as List<MySecondSet>).FirstOrDefault(y => y.MySecondSetKey == (Guid)x[0]) as T);
        }
        ...       
+4

, " ", . AsDbSet, , Find .

public static DbSet<T> AsDbSet<T>(this List<T> sourceList, Func<T, object> primaryKey = null) where T : class
{
    //all your other stuff still goes here

    if (primaryKey != null)
    {
        mockSet.Setup(set => set.Find(It.IsAny<object[]>())).Returns((object[] input) => sourceList.SingleOrDefault(x => (Guid)primaryKey(x) == (Guid)input.First()));
    }

    ...
}

, , guid , , , , , , ...

+3

:

public static class DbSetMocking
{
    #region methods

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>( this IReturns<TContext, DbSet<TEntity>> setup, ICollection<TEntity> entities, Func<object[], TEntity> find = null )
        where TEntity : class where TContext : DbContext
    {
        return setup.Returns( CreateMockSet( entities, find ).Object );
    }

    private static Mock<DbSet<T>> CreateMockSet<T>( ICollection<T> data, Func<object[], T> find )
        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() );

        mockSet.SetupData( data, find );

        return mockSet;
    }

    #endregion
}

:

private static MyRepository SetupRepository( ICollection<Type1> type1s, ICollection<Type2> type2s )
{
    var mockContext = new Mock<MyDbContext>();

    mockContext.Setup( x => x.Type1s ).ReturnsDbSet( type1s, o => type1s.SingleOrDefault( s => s.Secret == ( Guid ) o[ 0 ] ) );
    mockContext.Setup( x => x.Type2s ).ReturnsDbSet( type2s, o => type2s.SingleOrDefault( s => s.Id == ( int ) o[ 0 ] ) );

    return new MyRepository( mockContext.Object );
}
0

Entity Framework Core 2, .

, "Id". ( , .)

        //Find primary key. Here the PK must follow the convention "Class Name" + "Id" 
        Type type = typeof(T);
        string colName = type.Name + "Id";
        var pk = type.GetProperty(colName);
        if (pk == null)
        {
            colName = type.Name + "ID";
            pk = type.GetProperty(colName);
        }

, Pk, Find

        dbSet.Setup(x => x.Find(It.IsAny<object[]>())).Returns((object[] id) =>
        {
            var param = Expression.Parameter(type, "t");
            var col = Expression.Property(param, colName);
            var body = Expression.Equal(col, Expression.Constant(id[0]));
            var lambda = Expression.Lambda<Func<T, bool>>(body, param);
            return queryable.FirstOrDefault(lambda);
        });

So, you can see the full DbSet.Find layout support code below:

public static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
    {
        var queryable = sourceList.AsQueryable();
        var dbSet = new Mock<DbSet<T>>();

        dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
        dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
        dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
        dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
        dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));

        //Find primary key. Here the PK must follow the convention "Class Name" + "Id" 
        Type type = typeof(T);
        string colName = type.Name + "Id";
        var pk = type.GetProperty(colName);
        if (pk == null)
        {
            colName = type.Name + "ID";
            pk = type.GetProperty(colName);
        }

        dbSet.Setup(x => x.Find(It.IsAny<object[]>())).Returns((object[] id) =>
        {
            var param = Expression.Parameter(type, "t");
            var col = Expression.Property(param, colName);
            var body = Expression.Equal(col, Expression.Constant(id[0]));
            var lambda = Expression.Lambda<Func<T, bool>>(body, param);
            return queryable.FirstOrDefault(lambda);
        });

        return dbSet.Object;

    } //GetQueryableMockDbSet
0
source

Source: https://habr.com/ru/post/1656713/


All Articles