Giter VIP home page Giter VIP logo

romantitov / mockqueryable Goto Github PK

View Code? Open in Web Editor NEW
704.0 704.0 70.0 330 KB

Mocking Entity Framework Core operations such ToListAsync, FirstOrDefaultAsync etc

License: MIT License

C# 100.00%
automapper dotnet-core dotnet-framework dotnet-standard ef-core efcore entity-framework fakeiteasy favorite-mock-framework firstordefaultasync mocking moq netcore netstandard20 nsubstitute nunit tdd testing tolistasync unittests

mockqueryable's People

Contributors

bartdm avatar dlemstra avatar dmitriywind avatar gpzim98 avatar krilllind avatar movgp0 avatar olegkleyman avatar ramangreenflux avatar romantitov avatar sq735 avatar v0ldek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

mockqueryable's Issues

Unable to mock result of IAsyncEnumerable from DbSet using NSubstitute

Mocking an IAsyncEnumerable result from a DbSet using NSubstitute does not work and instead yield an empty result set.
A fix for this exact same issue was adressed in #25 but only for the Moq library.
Same override should be applied when using NSubstitute.

The following code snipped should return all users from a DbSet

List<UserEntity> users = new List<UserEntity> { new UserEntity() };
DbSet<UserEntity> mockDbSet = users.AsQueryable().BuildMockDbSet();
dbContext.Users.Returns(mockDbSet);

List<UserEntity> result = await userRepository.GetAllUsersAsync().ToListAsync(cancellationToken);

Assert.AreEqual(users.Count, result.Count);

Instead, an empty collection is returned.

IQueryable mock throws on ToListAsync()

This package looks useful. Unfortunately however, it doesn't solve the problem of calling async methods on IQueryable objects.

For example, calling .ToListAsync() on my mock queryable still throws:

System.InvalidOperationException : The source IQueryable doesn't implement IAsyncEnumerable<MyClass>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.

public class ApplicationControllerTests
{
    private static IEnumerable<Application> GetApplications() => new[]
    {
        new Application
        {
            ...
        },
        ...
    };

    [Fact]
    public async Task GetApplicationsReturnsExpected()
    {
        //Arrange
        var applications = GetApplications()
            .AsQueryable()
            .BuildMock();

        var fixture = ApplicationControllerFixture.Create();
        fixture.QueryService
            .Setup(s => s.GetApplications())
            .Returns(applications.Object);

        //Act
        var result = await fixture.Controller.GetApplications();

        //Assert
        var response = Assert.IsType<OkObjectResult>(result);
    }
}

[ApiController]
public class ApplicationController : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> GetApplications()
    {
        LogActivity(nameof(GetApplications), Request);
        var list = await QueryService.GetApplications(ClientId)
                        .ToListAsync();
        return Ok(list);
    }
}

Document usage without mocking framework?

For simple usage with no customization to the IQueryable<T> mock (such as the "How do I get started" example in the readme) there is no need for a mocking framework to be involved. Using TestAsyncEnumerableEfCore<T> directly is almost 1000x faster which was a very easy performance win for me :). My small test suite is about 15% faster now.

This issue is to request documenting TestAsyncEnumerableEfCore<T> in the readme. I would be happy to write it and submit a PR if that would be acceptable.

Thank you for this package!

Here's the benchmark I used and its results:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using MockQueryable.EntityFrameworkCore;
using MockQueryable.Moq;

BenchmarkRunner.Run(typeof(Program).Assembly);

[MemoryDiagnoser]
public class InitializeInstance
{
	private readonly IEnumerable<string> _input;

	public InitializeInstance()
	{
		_input = new[] { "1", "2", "3" };
	}
	
	[Benchmark(Baseline = true)]
	public IList<string> Moq()
	{
		var mock = _input.AsQueryable().BuildMock();
		return mock.Object.ToList();
	}

	[Benchmark]
	public IList<string> NoMoq()
	{
		return new TestAsyncEnumerableEfCore<string>(_input).ToList();
	}
}
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
Intel Core i9-9900 CPU 3.10GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.201
  [Host]     : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT
  DefaultJob : .NET 6.0.3 (6.0.322.12309), X64 RyuJIT

Method Mean Error StdDev Ratio Gen 0 Gen 1 Allocated
Moq 69,971.94 ns 670.265 ns 594.172 ns 1.000 2.0752 0.9766 17,951 B
NoMoq 70.09 ns 0.647 ns 0.605 ns 0.001 0.0248 - 208 B

Option to not load the navigation property

Do we already have this feature?

If no, I think this is necessery to make sure that Include statement is called or not in my repository or query.
The sample scenario:

public class Lesson {
  public int Id {get; set;}
}

public class Student {
  public int Id {get; set;}

  public List<Lesson> Lessons   { get; set; }
}

var student1 = new Student(){
  Id = 1,
  Lessons = new List<Lesson>(){
    new Lesson(){
      Id = 1,
    }
  }
}

var students = new List<Student>(){
  student1
};

var dataSetMock = students.AsQueryable().BuildMockDbSet();

var dbContextMock = new Mock<IDbContextInterface>();
dbContextMock.SetupGet(e => e.Students)
    .Returns(dataSetMock.Object);

var repository = new StudentsRepository(dbContextMock.Object);

var studentResult = repository.GetById("12312312");

studentResult.Lessons.Should().NotBeNull();

But this will causes studentResult == student1 returns false because we need to instansiate new object.

My suggestion for this, we can add new param in BuildMockDbSet

public static IQueryable<TEntity> BuildMock<TEntity>(this IQueryable<TEntity> data, bool loadNavigation= true) where TEntity : class {}

Thanks
:)

Supporting `EF.Functions.Like` and/or prossibility to provide cusotm `ExpressionVisitor`

Is there any chance that you can support the most common EF functions like EF.Functions.Like?

An relatively simple approach might be to write an ExpressionVisitor that replace those function calls with mock functions.

I think it would generally be a great idea to add an optional Visitor property to TestAsyncEnumerableEfCore<T> where you can pass a custom visitor that is used instead of the new TestExpressionVisitor(). This would allow developers to mock all sorts of custom functions and the like.

I would really appreciate this feature.

Cannot verify "Remove" method called on EF

Consider two simple tests (xunit):

    [Fact]
    public async void CreateFoo_ShouldReturnNewlyCreatedFoo()
    {
        var mockSet = Mocks.FooListMock.AsQueryable().BuildMockDbSet();
        var mockContext = new Mock<FooContext>(Options.Create(Mocks.CosmosDbMock));
        mockContext.Setup(m => m.Foos).Returns(mockSet.Object);
        
        Foos foosRepo = new Foos(mockContext.Object);
        var result = await foosRepo.CreateFoo(MockedFooObject);

        mockSet.Verify(m => m.Add(It.IsAny<Foo>()), Times.Once());  // <-- This works
        mockContext.Verify(m => m.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
    }

    [Fact]
    public async void DeleteFoo_WithValidId_ShouldReturnId()
    {
        var mockSet = Mocks.FooListMock.AsQueryable().BuildMockDbSet();
        var mockContext = new Mock<FooContext>(Options.Create(Mocks.CosmosDbMock));
        mockContext.Setup(m => m.Foos).Returns(mockSet.Object);
        
        Foos foosRepo = new Foos(mockContext.Object);
        var result = await foosRepo.DeleteFoo("id");

        mockSet.Verify(m => m.Remove(It.IsAny<Foo>()), Times.Once());  // <-- This doesn't. It says it was called 0 times.
        mockContext.Verify(m => m.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
    }

Here are the methods under test:

        public async Task<Foo> CreateFoo(Foo foo) {
            _context.Foos.Add(foo);
            await _context.SaveChangesAsync();

            return foo;
        }

        public async Task<string?> DeleteFoo(string id) {
            var foo = await _context.Foos.SingleOrDefaultAsync(p => p.FooId == id);
            if (foo != null) {
                _context.Foos.Remove(foo);
                await _context.SaveChangesAsync();
                return id;
            } else {
                return null;
            }
        }

Notice in the second test when verifying the Remove method was called once, it fails saying it was called 0 times. However, if I comment that line out, the verify on the SaveChangesAsync passes. What gives?

ToListAsync() returns empty list after package update to 6.0.0

After having updated the MockQueryable package to version 6.0.0 some of my unit tests fail because .ToListAsync() now returns an empty list.
Example code:

DbSet<Sections> mockSections = sections.AsQueryable().BuildMockDbSet();
_dbContext.Sections.Returns(mockSections); 
List<Section> sections = await _dbContext.Sections.ToListAsync(cancellationToken); 

The last line returns an empty list with the new package version. Why?

The failed tests and the full test code can be found here

Not suported with EF7: ExecuteDeleteAsync and ExecuteUpdateAsync

Hello!

I've been using MockQueryable with .NET6 a lot
Recently my project migrated to .NET 7 (EF Core 7)
And I started to use new EF7 features:

  • ExecuteDeleteAsync
            ...
            .Where(user => user.Id == id)
            .ExecuteDeleteAsync();

ExecuteDeleteAsync implementation is here
https://github.com/dotnet/efcore/blob/main/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs#L316

  • ExecuteUpdateAsync
            .Where(user => user.Id == id)
            .ExecuteUpdateAsync(updater => updater
                .SetProperty(
                    user => user.Status,
                    user => status)
                .SetProperty(
                    user => user.ModifiedBy,
                    user => _userContextProvider.GetUserId())
                .SetProperty(
                    user => user.ModifiedOn,
                    user => DateTimeOffset.UtcNow));

ExecuteUpdateAsync implementation is here
https://github.com/dotnet/efcore/blob/main/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs#L372

I use MockQueryable using a standard pattern:

           var mock = users.BuildMock();
           _userRepository.Setup(x => x.GetQueryable()).Returns(mock);

ToListAsync(), FirstOrDefaultAsync() and others works perfectly with this pattern.

Unfortunately ExecuteUpdateAsync / ExecuteDeleteAsync doesn't work at all :(

When I try to run a unit test, a mocked GetQueryable method works nicely as usual, but it throws an exception on ExecuteDeleteAsync

 <System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.InvalidOperationException: There is no method 'ExecuteDelete' on type 'Microsoft.EntityFrameworkCore.RelationalQueryableExtensions' that matches the specified arguments
   at System.Linq.EnumerableRewriter.FindMethod(Type type, String name, ReadOnlyCollection`1 args, Type[] typeArgs)
   at System.Linq.EnumerableRewriter.VisitMethodCall(MethodCallExpression m)
   at System.Linq.EnumerableExecutor`1.Execute()
   at System.Linq.EnumerableQuery`1.System.Linq.IQueryProvider.Execute[TElement](Expression expression)
   at Microsoft.EntityFrameworkCore.RelationalQueryableExtensions.ExecuteDelete[TSource](IQueryable`1 source)
   at lambda_method34(Closure)
   at MockQueryable.Core.TestQueryProvider`1.CompileExpressionItem[TResult](Expression expression)
   at MockQueryable.Core.TestQueryProvider`1.Execute[TResult](Expression expression)
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at MockQueryable.EntityFrameworkCore.TestAsyncEnumerableEfCore`1.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.RelationalQueryableExtensions.ExecuteDeleteAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)

Exception throws here on .Invoke(this, new object[] { expression });
https://github.com/romantitov/MockQueryable/blob/master/src/MockQueryable/MockQueryable.EntityFrameworkCore/TestQueryProviderEfCore.cs#L25

Same exception throws at ExecuteUpdateAsync (but it says There is no method 'ExecuteUpdate' on type 'Microsoft.EntityFrameworkCore.RelationalQueryableExtensions' that matches the specified arguments)

The custom logic pattern doesn't work because ExecuteUpdateAsync / ExecuteDeleteAsync are IQueryable extensions

Thank you very much

System.MissingMethodException: Method not found: 'System.__Canon NSubstitute.Arg.Any()'.

I've upgraded to the latest release to have the support for Entity Framework Core 3.0 but I receive the following error when running my unit tests:

System.MissingMethodException: Method not found: 'System.__Canon NSubstitute.Arg.Any()'.

Using NSubstitute version 4.2.1.
Running on macOS.

Part of the unit test code:

var myQueryableMock = myList.AsQueryable().BuildMock();
myRepositorySubstitute.Get(Arg.Any<Expression<Func<MyEntity, bool>>>()).Returns(myQueryableMock);

ToListAsync() returns empty list on second call

Hello,
I am running into an issue where the ToListAsync() returns an empty list the second time it is called.

The issue can be easily reproduced by modifying one of the unit tests of your project,
adding a second call to the GetAll() function:

[TestCase]
public async Task DbSetGetAllUserEntity()
{
    //arrange
    var users = CreateUserList();
    var mock = users.AsQueryable().BuildMockDbSet();
    var userRepository = new TestDbSetRepository(mock.Object);
    //act
    var result = await userRepository.GetAll();
    result = await userRepository.GetAll();
    //assert
    Assert.AreEqual(users.Count, result.Count);
}

RE: IQueryable mock throws on ToListAsync()

This issue is related to #32 but there was no resolution given there so I am opening a new issue.

When mocking an IQueryable, I am getting the exception

System.InvalidOperationException : The source IQueryable doesn't implement IAsyncEnumerable<Event>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.

My code is structured as follows:

// Method Under Test
public async Task<List<Event>> GetEvents(FilterOptions filterOptions)
{
	var queryable = GetEventsQueryable();

	queryable = FilterByDate(queryable, filterOptions);
	// queryable = FilterByLocation(), FilterBy...(), etc

	var events = await queryable
		.ToListAsync()
		.ConfigureAwait(false);

	return events;
}

public IQueryable<Event> GetEventQueryable()
{
	try
	{
		return MyDbContext
			.EventData
			.AsQueryable();
	}
	catch
	{
		...
	}
}

public IQueryable<Event> FilterByDate(
	IQueryable<Event> queryable,
	FilterOptions options)
{
	if (options.StartDate != null)
	{
		queryable = queryable.Where(e => e.EventDate >= options.StartDate);
	}

	if (options.EndDate != null)
	{
		queryable = queryable = queryable.Where(e => e.EventDate <= options.EndDate);
	}

	return queryable;
}

// Test
private List<Event> Events
{
	get
	{
		new List<Event>
		{
			...
		};
	}
}

[Fact]
public void GetEvents_DefaultFilterOptions_ReturnsAllEvents()
{
	// Arrange
	var mockQueryable = Events.AsQueryable().BuildMock();

	var expectedEvents = new List<Event>
	{
		...
	};
	
	...
	mockCaller
		.Setup(m => m.GetEventsQueryable())
		.Returns(mockQueryable);

	mockCaller
		.Setup(m => m.FilterByDate(It.IsAny<IQueryable<Event>>(), It.IsAny<FilterOptions>()))
		.Returns(mockQueryable);

	var eventManager = new EventManager();

	var filterOptions = new EventFilterOptions();

	// Act
	var actualEvents = eventManager.GetEvents(filterOptions);

	// Assert
	Assert.Equal(expectedEvents, actualEvents);
}

When the test gets to the events = await queryable.ToListAsync() line it throws the System.InvalidOperationException.

I am not sure what is going wrong or how to fix the issue. I started looking into using the TestAsyncEnumerableEfCore class and trying to return this instead of the mocked queryable, but the issue still persisted.

Support for .NET 5 ?

Any current plans to yet?

Trying the FakeItEasy flavour it in a .NET 5 RC2 project and seems to fail with:

System.IO.FileNotFoundException : Could not load file or assembly 'Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.

... which I install, but then it fails with:

Result Message:	System.TypeLoadException : Could not load type 'Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider' from assembly 'Microsoft.EntityFrameworkCore, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.

Which I'm guessing might be a namespace difference between core 3.1 and 5 ?

code is var fakeQueryable = new List<Enquiry>().AsQueryable().BuildMock();

how would I get firstordefaultasync working?

  • error: .FirstOrDefaultAsync) may not be used in setup / verification expressions
_mockContext = new Mock<ApplicationDbContext>(options);
var data = items.AsQueryable();
var mockSet = data.BuildMockDbSet();

mockSet.Setup(x => x.FirstOrDefaultAsync(It.IsAny<System.Linq.Expressions.Expression<Func<Notes, bool>>>(), It.IsAny<System.Threading.CancellationToken>())).ReturnsAsync((object pred) =>
            {
                var predicate = (System.Linq.Expressions.Expression<Func<Notes, bool>>)pred;
                var r = (Notes)data.FirstOrDefault(pred);
                return r;
            });
            
 mockSet.Setup(x => x.GetQueryable()).Returns(mockSet.Object);
            _mockContext.Setup(m => m.Notes).Returns(mockSet.Object);

Modifying base collection throws System.InvalidOperationException

Consider:

var emails = new List<EmailQueueEntry>();
emails.Add(new EmailQueueEntry());
var mockSet = emails.AsQueryable().BuildMockDbSet();
emails.Add(new EmailQueueEntry());
var list= await mockSet.Object.ToListAsync();

This used to work fine in v5.0.1, but since upgrading our projects to .net6 and MockQueryable to 6.0.1 now throws System.InvalidOperationException : Collection was modified; enumeration operation may not execute on the ToListAsync call.

Seems to be a regression of this

Checked also with v7.0 does the same thing. I think it may be the method CreateAsyncMock method in MoqExtensions.cs added for v6.0.1 causing the issue.

After upgrade it does not work with EF.Functions.Like

I have updated my project from .NET core 3x to .NET 5, also updated Moq and MockQueryable for Moq and Im getting

System.InvalidOperationException : The 'Like' method is not supported because the query has switched to client-evaluation. This usually happens when the arguments to the method cannot be translated to server. Rewrite the query to avoid client evaluation of arguments so that method can be translated to server.

Set return value of FirstOrDefaultAsync method by NSubstitute returns error

When I want to set a return value (null or none-null value) for FindOrDefaultAsync method I get the following error:
Can not return value of type Task`1 for IQueryable.get_Expression (expected type Expression).

[Fact]
public async Task FindRole_WhenCall_ReturnsIdentityRole()
{
    // Arrange
    var iRole = new IdentityRole();
    var dbset = new List<IdentityRole>()
    {
        new IdentityRole()
    }
    .AsQueryable().BuildMockDbSet();
    
    dbset.FirstOrDefaultAsync(role => role.Name=="a")
        .ReturnsNull();

    _repository.Query().Returns(dbset);
    
    // Act
    var result = await _rolesService.FindRole("RoleName");

    // Assert
    result.Should().BeNull();
}

image

BuildMock() extension not working with EntityFrameworkCore 5.0.0

Describe the bug
Recently I've migrated my project to .net 5.0 with EF Core 5.0.
The BuildMock Extension now throws this error message:
System.TypeLoadException : Could not load type 'Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider' from assembly 'Microsoft.EntityFrameworkCore, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.

To Reproduce
Here is a small sniped of the code that throws the exception:
var users = (new[] { new User(), new User(), new User() }).AsQueryable().BuildMock();

Expected behavior
No exception generated. The mocked collection is created.

Environment:

  • MockQueryable.NSubstitute version: [e.g. 3.1.3]
  • NSubstitute version: [e.g. 4.2.2]
  • Platform: [e.g. dotnet5.0 project on Windows with Visual Studion 2019 v16.8.0]

System.InvalidOperationException in Empty mock.

Hi,

When I try to use BuildMock().Object with an empty list, I've got the following exception. In my case, I use ToListAsync() method of EF Core.

System.InvalidOperationException: The source 'IQueryable' doesn't implement 'IAsyncEnumerable<Kazandirio.CampaignRepository.Model.Campaign>'. Only sou...

_campaignUnitOfWorkMock.Setup(a => a.GetQueryable<Campaign>()) .Returns(() => new List<Campaign>().AsQueryable().BuildMock().Object);

Several Interface changes need to be accounted for with Entity Framework Core 3.0 and .NET Core 3.0

Using EntityFrameworkCore 3.0.0-preview7.19362.6 The IAsyncEnumerable IAsyncQueryProvider and IAsyncEnumerator interfaces require different members.

As far as I can tell the member changes are as follows:

IAsyncEnumerable:

  • Adds GetAsyncEnumerator(CancellationToken)

IAsyncQueryProvider:

  • Adds ExecuteAsync<TResult>(Expression, CancellationToken) with return type of TResult

IAsyncEnumerator:

  • Adds MoveNextAsync()

IAsyncDisposable:

  • Adds DisposeAsync()

As far as I can tell, the ExecuteAsync() methods that return IAsyncEnumerable or Task<TResult> are unnecessary now.

These interface changes also appear to break some of the non-core packages as well, apparrently due to the removal of the GetEnumerator method on IAsyncEnumerable (I believe this just needs to be changed to GetAsyncEnumerator instead)

related GitHub issue:
dotnet/efcore#12047

Does mocking SingleOrDefaultAsync not work for FakeItEasy ?

I'm trying to mock the SingleOrDefaultAsync method with FakeItEasy but I get the error:

"FakeItEasy0002: Member SingleOrDefaultAsync can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted."

Here is the my code:

        var claimants = fixture.CreateMany<Claimant>();
        var claimantsDbSet = claimants.AsQueryable().BuildMockDbSet();
        A.CallTo(() => claimantsDbSet.SingleOrDefaultAsync(A<Expression<Func<Claimant, bool>>>.Ignored, A<CancellationToken>.Ignored))
            .Returns(claimant);

Is this not possible with FakeItEasy?

Allow for mutation of base collection

Currently when mutating the base collection such that:

var userEntities = new List<UserEntity>();
var set = userEntities.AsQueryable().BuildMockDbSet()
userEntities.Add(new UserEntity());
set.Single();

results in the following exception:

System.InvalidOperationException : Collection was modified; enumeration operation may not execute.

It would be good to allow for mutation of the base enumerable when possible.

Parameterized Repository Query Issue

I have problem while mocking my repositories with query.

Repository Class Section:

public IQueryable<TEntity> GetQuery(Expression<Func<TEntity, bool>> filter)
{
   return _dbContext.Set<TEntity>().Where(filter).AsQueryable();
}

Test Class Section:

...
var alarmRepositoryMock = new Mock<IAlarmRepository>();

var a1 = new Alarm 
            {
                Code = 101,
                Message = "Alarm!"
            };
List<Alarm> alarms = new List<Alarm>();
alarms.Add(new Alarm 
   {
       Id = new Guid(),
       Code = a1.Code,
       Message = a1.Message
    });

var alarmsQueryable = alarms.AsQueryable().BuildMock();
alarmRepositoryMock.Setup(x => x.GetQuery(x => x.Code == 101)).Returns(alarmsQueryable.Object);
...

Service Class Section:

public async Task<string> AlarmAddOrUpdateAsync(AlarmDto model)
{
- var findAlarm = await _alarmRepository.GetQuery(x => x.Code == model.Code).FirstOrDefaultAsync();
...
}

When I update it as below, the problem disappears.

+ var findAlarm = await _alarmRepository.GetQuery(x => x.Code == 101).FirstOrDefaultAsync();

How can i parameterized this query. I want to be independent from Code value.

Can I use AddAsync and expect to have the result in the mocked DbSet?

Hi I use this library and now I have encountered an issue, in one of my tests I need to trigger a class that behind the scene uses AddAsync to add the item I am passing in, then using the same context.dbset I am trying to get it but the dbset does not contain the element, only preexisting objects.
Is this scenario not supported?

Thank you

EF Core 3.1: New Overrides

Protip: When supporting EF Core 3.1, mocked DbSet<> objects need to additionally override the newly-added methods .AsQueryable() and .AsAsyncEnumerable() (which simply return this;) within BuildMockDbSet() calls.

Anyone who (like me) was using the .AsQueryable() extension method upon a DbSet<> in production code will, after upgrade to 3.1, be calling the methods upon DbSet<> instead. In my case, this meant that instead of getting a cast of the DbSet<> to IQueryable<> I was getting an auto-generated default implementation from Moq's DefaultValueProvider, which is basically Array.Empty<T>.AsQueryable().

FindAsync is not working properly

I use BuildMockDbSet() method to create DbSet mock.
I have a simple class representing entity:

 public class Feature {
        [Key]
        public int Id { get; set; }
        [Required]
        [StringLength(255)]
        public string Name { get; set; }
    }

I see that FindAsync(Id) method is not working properly and returns null every time in spite of elements are existing and same predicate is working with EF DbContext. Have replaced with FirstOrDefault() for a while. Could you please take a look at this?

No license listed

What license are you planning to use for this code? The project I would like to use this nuget package in has some restrictions about what types of licenses we can use. Thanks.

FindAsync is not working properly

I saw it, similar has been discussed already, but it does still not work on my end

I have

var countries = CountryStub.GetCountries().AsQueryable().BuildMockDbSet(); // 3 items (1st is Id:1)
            _uow.Setup(x => x.Context.Set<Country>()).Returns(countries.Object);
            _uow.Setup(x => x.Context.Set<Country>().FindAsync(1)).ReturnsAsync(new Country { Id:1 }); // and I dont want to do this, collection contains that item already
            var repository = new CountryRepository(_uow.Object);
            var result = await repository.FindByIdAsync(1); //it calls FindAsync internally and result is null

and I would expect once I mock context Country that FindAsync method should return item with id:1

Mocking AddAsync

I need to mock AddAsync(obj) function.
I have seen this stackoverflow topic, but proposed code:

mockWebJobDbSet
    .Setup(_ => _.AddAsync(It.IsAny<WebJobStatus>(), It.IsAny<System.Threading.CancellationToken>()))
    .Callback((WebJobStatus model, CancellationToken token) => { webjobstatusList.Add(model); })
    .Returns((WebJobStatus model, CancellationToken token) => Task.FromResult((EntityEntry<WebJobStatus>)null));

will return null EntityEntry<WebJobStatus> which will cause to fail my unit test.

Function I want to test:

Url obj = new()
{
    FullUrl = url
};

var savedObj = await _context.Url.AddAsync(obj);

return savedObj.Entity.Id

Error: savedObj is null

AutoMapper .ProjectTo fix

AutoMapper casts an IQueryable to IQueryable which breaks the current implementation of CreateQuery in the TestAsyncEnumerable class. I have got this working by adding the following into the public IQueryable CreateQuery(Expression expression) method:

public IQueryable CreateQuery(Expression expression)
{    
    if (expression is MethodCallExpression m)
    {
        var resultType = m.Method.ReturnType; // it shoud be IQueryable<T>
        var tElement = resultType.GetGenericArguments()[0];
        var queryType = typeof(TestAsyncEnumerable<>).MakeGenericType(tElement);
        return (IQueryable)Activator.CreateInstance(queryType, expression);
    }
    return new TestAsyncEnumerable<T>(expression);
}

Strong Name

Is it possible to get this package created with a strong name?

Entry method of mocked context returns null

I'm using a generic repository pattern that has an UpdateAsync method to update an entity in EF Core.

public async Task UpdateAsync(T entity)
{
    context.Entry(entity).State = EntityState.Modified;
    await context.SaveChangesAsync();
}

When I'm mocking the context using MockQueryable in a unit test, the call to the Entry method of the context returns null.
How should I mock the Entry method?
The Entry method returns an EntityEntry object; the constructor of EntityEntry is taking an InternalEntityEntry object that is internal to EF Core.

Here's my unit test using xUnit and Moq:

[Fact]
public async void UpdateAsync_SavesObject()
{
     //Arrange
     var persons = new List<Person>
     {
          new Person { Id = 1, FirstName = "Ken", MiddleName = "J", LastName = "Sánchez" },
          new Person { Id = 2, FirstName = "Terri", MiddleName = "Lee", LastName = "Duffy" }
     };
     var mockSet = persons.AsQueryable().BuildMockDbSet();

     var mockContext = new Mock<AWContext>();
     mockContext.Setup(x => x.Set<Person>())
         .Returns(mockSet.Object);
     var repository = new EfRepository<Person>(mockContext.Object);

      //Act
      var existingPerson = new Person { Id = 1, FirstName = "Ken", MiddleName = "C", LastName = "Sánchez" };
      await repository.UpdateAsync(existingPerson);

     //Assert
      mockContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()));
}

Not suported with EF6: BulkUpdateAsync, BulkDeleteAsync, SingleUpdateAsync.

Hi Guys.
I use MockQueryable approach for EF async methods testing.
But I've noticed that it doesn't work for async methods for collections, exactly 'bulk extensions': https://entityframework-extensions.net/bulk-update

Message:  System.AggregateException : One or more errors occurred. (Field '_queryCompiler' defined on type 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider' is not a field on the target object which is of type 'TestAsyncEnumerableEfCore[TEntity]'.) ---- System.ArgumentException : Field '_queryCompiler' defined on type 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider' is not a field on the target object which is of type 'TestAsyncEnumerableEfCore[TEntity]'.

After some investigation, I found out that Bulk extensions methods require using EntityQueryProvider as provider and IQueryCompiler as _queryCompiler field.

I use the next code to set up async EF methods for testing:

private Mock<DbSet<TEntity>> DbSetConfigureForAsync<TEntity>(Mock<DbSet<TEntity>> dbSet, IQueryable<TEntity> data)
    where TEntity : class
{
    var enumerable = new TestAsyncEnumerable<TEntity>(data);

    // Configure queryable calls
    dbSet.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(enumerable);
    dbSet.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(data.Expression);
    dbSet.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(data.ElementType);
    dbSet.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

    // Configure async enumerable calls
    dbSet.As<IAsyncEnumerable<TEntity>>()
        .Setup(m => m.GetAsyncEnumerator(It.IsAny<CancellationToken>()))
        .Returns(enumerable?.GetAsyncEnumerator());

    // Configure DbSet calls
    dbSet.Setup(m => m.AsQueryable()).Returns(enumerable); // alternative: _mockSet.Object
    dbSet.Setup(m => m.AsAsyncEnumerable()).Returns(CreateAsyncMock(data));

    return dbSet;
}

Thanks.

DotNet5.0 Compatibility

Tried upgrading to .net5.0 preview6 .

I get the following error: System.IO.FileNotFoundException : Could not load file or assembly 'Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.

After Installing Microsoft.Bcl.AsyncInterfaces from Nuget I get:
System.TypeLoadException : Could not load type 'Microsoft.EntityFrameworkCore.Query.Internal.IAsyncQueryProvider' from assembly 'Microsoft.EntityFrameworkCore, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.

Return value of SaveChangesAsync

In my test i use

mockContext.Verify(_ => _.SaveChangesAsync(), Times.Once());

to verify that SaveChangesAsync() is called

in the function i test. I check if the SaveChanges call is successful by its result

int results = await _context.SaveChangesAsync(CancellationToken.None);
if (results >= 1)
{
	return new OkObjectResult();
} 
return new BadRequestObjectResult();

While testing my function fails as SaveChangesAsync always returns 0;

I'm i doing something wrong? or is This a Bug?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.