Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dotnetcore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
dotnet-version: '8.0.x'

- name: Build with dotnet
run: dotnet build src/MockQueryable/*.sln --configuration Release
Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Do you prefer *DbSet*?

```csharp
//2 - build mock by extension
var mock = users.AsQueryable().BuildMockDbSet();
var mock = users.BuildMockDbSet();

//3 - setup DbSet for Moq
var userRepository = new TestDbSetRepository(mock.Object);
Expand All @@ -77,7 +77,7 @@ var users = new List<UserEntity>
new UserEntity{Id = userId,LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012")},
//etc.
};
var mock = users.AsQueryable().BuildMockDbSet();
var mock = users.BuildMockDbSet();

//Aditional setup for FindAsync
mock.Setup(x => x.FindAsync(userId)).ReturnsAsync((object[] ids) =>
Expand All @@ -91,6 +91,22 @@ var userRepository = new TestDbSetRepository(mock.Object);
var user = await ((DbSet<UserEntity>) userRepository.GetQueryable()).FindAsync(userId);
```

You can also add your custom expression visitor with custom logic:

```C#

var users = new List<UserEntity>
{
new UserEntity{Id = userId,LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012")},
//etc.
};

//Bould mock with custom SampleLikeExpressionVisitor, that emulates EF.Functions.Like
var mockDbSet = users.BuildMockDbSet<UserEntity, SampleLikeExpressionVisitor>();
var userRepository = new TestDbSetRepository(mockDbSet.Object);

```

Check out the [sample project](https://github.com/romantitov/MockQueryable/tree/master/src/MockQueryable/MockQueryable.Sample)

### Where can I get it?
Expand Down
13 changes: 8 additions & 5 deletions src/MockQueryable/MockQueryable.Core/MockQueryable.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net8</TargetFramework>
<PackageId>MockQueryable.Core</PackageId>
<Authors>Roman Titov</Authors>
<Description>
Expand All @@ -14,11 +14,14 @@
<PackageTags>Mock EntityFrameworkCore Queryable mock EF UnitTests EntityFrameworkCore</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReleaseNotes>
#80 Altered namespace for extension method to revert a breaking change - Thanks @StevePy
#81 Add support to ExecuteDelete and ExecuteUpdate - Thanks @lazaro-ansaldi and @Catlandor
#82 Update versions of Moq, NSubstitute and FakeItEasy to newer versions without known vulnerabilities - Thanks @Catlandor
#73 Supporting EF.Functions.Like and/or prossibility to provide cusotm ExpressionVisitor - Thanks @Catlandor
#66 Not suported with EF7: ExecuteDeleteAsync and ExecuteUpdateAsync - Thanks @Catlandor
</PackageReleaseNotes>
<Version>7.0.3</Version>
<AssemblyVersion>7.0.0.3</AssemblyVersion>
<FileVersion>7.0.0.3</FileVersion>
<Version>8.0.0</Version>
<AssemblyVersion>8.0.0.0</AssemblyVersion>
<FileVersion>8.0.0.0</FileVersion>
<Company></Company>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand Down
138 changes: 70 additions & 68 deletions src/MockQueryable/MockQueryable.Core/TestQueryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,82 @@
using System.Linq;
using System.Linq.Expressions;

namespace MockQueryable.Core
namespace MockQueryable.Core;

public abstract class TestQueryProvider<T, TExpressionVisitor> : IOrderedQueryable<T>, IQueryProvider
where TExpressionVisitor : ExpressionVisitor, new()
{
public abstract class TestQueryProvider<T> : IOrderedQueryable<T>, IQueryProvider
{
private IEnumerable<T> _enumerable;
private IEnumerable<T> _enumerable;


protected TestQueryProvider(Expression expression)
{
Expression = expression;
}
{
Expression = expression;
}

protected TestQueryProvider(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
Expression = enumerable.AsQueryable().Expression;
}

public IQueryable CreateQuery(Expression expression)
{
if (expression is MethodCallExpression m)
{
var resultType = m.Method.ReturnType; // it should be IQueryable<T>
var tElement = resultType.GetGenericArguments().First();
return (IQueryable) CreateInstance(tElement, expression);
}

return CreateQuery<T>(expression);
}

public IQueryable<TEntity> CreateQuery<TEntity>(Expression expression)
{
return (IQueryable<TEntity>) CreateInstance(typeof(TEntity), expression);
{
_enumerable = enumerable;
Expression = enumerable.AsQueryable().Expression;

}

public IQueryable CreateQuery(Expression expression)
{
if (expression is MethodCallExpression m)
{
var resultType = m.Method.ReturnType; // it should be IQueryable<T>
var tElement = resultType.GetGenericArguments().First();
return (IQueryable)CreateInstance(tElement, expression);
}

return CreateQuery<T>(expression);
}

public IQueryable<TEntity> CreateQuery<TEntity>(Expression expression)
{
return (IQueryable<TEntity>)CreateInstance(typeof(TEntity), expression);
}

private object CreateInstance(Type tElement, Expression expression)
protected abstract object CreateInstance(Type tElement, Expression expression);


public object Execute(Expression expression)
{
var queryType = GetType().GetGenericTypeDefinition().MakeGenericType(tElement);
return Activator.CreateInstance(queryType, expression);
}

public object Execute(Expression expression)
{
return CompileExpressionItem<object>(expression);
}

public TResult Execute<TResult>(Expression expression)
{
return CompileExpressionItem<TResult>(expression);
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
if (_enumerable == null) _enumerable = CompileExpressionItem<IEnumerable<T>>(Expression);
return _enumerable.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
if (_enumerable == null) _enumerable = CompileExpressionItem<IEnumerable<T>>(Expression);
return _enumerable.GetEnumerator();
}

public Type ElementType => typeof(T);

public Expression Expression { get; }

public IQueryProvider Provider => this;

private static TResult CompileExpressionItem<TResult>(Expression expression)
{
var visitor = new TestExpressionVisitor();
var body = visitor.Visit(expression);
var f = Expression.Lambda<Func<TResult>>(body ?? throw new InvalidOperationException($"{nameof(body)} is null"), (IEnumerable<ParameterExpression>) null);
return f.Compile()();
}
}
return CompileExpressionItem<object>(expression);
}

public virtual TResult Execute<TResult>(Expression expression)
{

return CompileExpressionItem<TResult>(expression);
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
_enumerable ??= CompileExpressionItem<IEnumerable<T>>(Expression);
return _enumerable.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
_enumerable ??= CompileExpressionItem<IEnumerable<T>>(Expression);
return _enumerable.GetEnumerator();
}

public Type ElementType => typeof(T);

public Expression Expression { get; }

public IQueryProvider Provider => this;

private static TResult CompileExpressionItem<TResult>(Expression expression)
{
var visitor = new TExpressionVisitor();
var body = visitor.Visit(expression);
var f = Expression.Lambda<Func<TResult>>(
body ?? throw new InvalidOperationException($"{nameof(body)} is null"),
(IEnumerable<ParameterExpression>)null);
return f.Compile()();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6</TargetFramework>
<TargetFramework>net8</TargetFramework>
<PackageId>MockQueryable.EntityFrameworkCore</PackageId>
<Authors>Roman Titov</Authors>
<Description>
Expand All @@ -15,11 +15,14 @@
<PackageTags>Mock EntityFrameworkCore Queryable mock EF UnitTests EntityFrameworkCore</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReleaseNotes>
#80 Altered namespace for extension method to revert a breaking change - Thanks @StevePy
#81 Add support to ExecuteDelete and ExecuteUpdate - Thanks @lazaro-ansaldi and @Catlandor
#82 Update versions of Moq, NSubstitute and FakeItEasy to newer versions without known vulnerabilities - Thanks @Catlandor
#73 Supporting EF.Functions.Like and/or prossibility to provide cusotm ExpressionVisitor - Thanks @Catlandor
#66 Not suported with EF7: ExecuteDeleteAsync and ExecuteUpdateAsync - Thanks @Catlandor
</PackageReleaseNotes>
<Version>7.0.3</Version>
<AssemblyVersion>7.0.0.3</AssemblyVersion>
<FileVersion>7.0.0.3</FileVersion>
<Version>8.0.0</Version>
<AssemblyVersion>8.0.0.0</AssemblyVersion>
<FileVersion>8.0.0.0</FileVersion>
<Company></Company>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand All @@ -45,7 +48,7 @@
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.7" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using MockQueryable.EntityFrameworkCore;
using MockQueryable.Core;
using MockQueryable.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

// Moving MockQueryableExtensions BuildMock into the MockQueryable.EntityFrameworkCore
// namespace had breaking changes with earlier extensions added to MockQueryable.Moq
Expand All @@ -9,11 +11,19 @@
// is dependent on the EF Core AsyncEnumerable.
namespace MockQueryable
{
public static class MockQueryableExtensions
public static class MockQueryableExtensions
{
public static IQueryable<TEntity> BuildMock<TEntity>(this ICollection<TEntity> data)
where TEntity : class
{
public static IQueryable<TEntity> BuildMock<TEntity>(this IEnumerable<TEntity> data) where TEntity : class
{
return new TestAsyncEnumerableEfCore<TEntity>(data);
}
return new TestAsyncEnumerableEfCore<TEntity, TestExpressionVisitor>(data, entity => data.Remove(entity));
}

public static IQueryable<TEntity> BuildMock<TEntity, TExpressionVisitor>(this ICollection<TEntity> data)
where TEntity : class
where TExpressionVisitor : ExpressionVisitor, new()
{
return new TestAsyncEnumerableEfCore<TEntity, TExpressionVisitor>(data, entity => data.Remove(entity));
}
}
}
Loading