Skip to content

Commit

Permalink
Merge pull request #57 from romantitov/TestAsyncEnumerableEfCore
Browse files Browse the repository at this point in the history
Use TestAsyncEnumerableEfCore as mock
  • Loading branch information
romantitov authored Mar 24, 2022
2 parents b85f971 + 9ca52ce commit 1ea5e1c
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 181 deletions.
283 changes: 135 additions & 148 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,148 +1,135 @@

# MockQueryable

[![Build status](https://github.com/romantitov/MockQueryable/workflows/.NET%20Core/badge.svg)](https://github.com/romantitov/MockQueryable/actions)
[![Build status](https://ci.appveyor.com/api/projects/status/ggdbipcyyfb4av9e?svg=true)](https://ci.appveyor.com/project/handybudget/mockqueryable)
[![Build Status](https://travis-ci.org/romantitov/MockQueryable.svg?branch=master)](https://travis-ci.org/romantitov/MockQueryable)
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.Moq.svg)](https://www.nuget.org/packages/MockQueryable.Moq/)
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.NSubstitute.svg)](https://www.nuget.org/packages/MockQueryable.NSubstitute/)
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.FakeItEasy.svg)](https://www.nuget.org/packages/MockQueryable.FakeItEasy/)
[![License](https://img.shields.io/github/license/romantitov/MockQueryable.svg)](https://github.com/romantitov/MockQueryable/blob/master/LICENSE)

[![Build history](https://buildstats.info/appveyor/chart/handybudget/mockqueryable)](https://ci.appveyor.com/project/handybudget/mockqueryable/history)



Extensions for mocking [Entity Framework Core](https://github.com/aspnet/EntityFrameworkCore/) (EFCore) operations such ToListAsync, FirstOrDefaultAsync etc. by [Moq](https://github.com/moq/moq), [NSubstitute](http://nsubstitute.github.io/) or [FakeItEasy](https://fakeiteasy.github.io/)
When writing tests for your application it is often desirable to avoid hitting the database. The extensions allow you to achieve this by creating a context – with behavior defined by your tests – that makes use of in-memory data.

### When should I use it?

If you have something similar to the following code:
```csharp
var query = _userRepository.GetQueryable();

await query.AnyAsync(x =>...)
await query.FirstOrDefaultAsync(x =>...)
query.CountAsync(x => ...)
query.ToListAsync()
//etc.
```
and you want to cover it by unit tests

### How do I get started?

```csharp
//1 - create a List<T> with test items
var users = new List<UserEntity>()
{
new UserEntity{LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012")},
...
};

//2 - build mock by extension
var mock = users.AsQueryable().BuildMock();

//3 - setup the mock as Queryable for Moq
_userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object);

//3 - setup the mock as Queryable for NSubstitute
_userRepository.GetQueryable().Returns(mock);

//3 - setup the mock as Queryable for FakeItEasy
A.CallTo(() => userRepository.GetQueryable()).Returns(mock);
```

Do you prefer *DbSet*?

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

//3 - setup DbSet for Moq
var userRepository = new TestDbSetRepository(mock.Object);

//3 - setup DbSet for NSubstitute or FakeItEasy
var userRepository = new TestDbSetRepository(mock);
```

Do you use *DbQuery*?

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

//3 - setup the mock as Queryable for Moq
_userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object);

//3 - setup the mock as Queryable for NSubstitute
_userRepository.GetQueryable().Returns(mock);

//3 - setup the mock as Queryable for FakeItEasy
A.CallTo(() => userRepository.GetQueryable()).Returns(mock);
```
### Can I extend the mock object created by MockQueryable with custom logic?
MockQueryable creates for your tests a mock object based on in-memory data, but you can also add some custome logic to it.

``` C#
var userId = Guid.NewGuid();
var users = new List<UserEntity>
{
new UserEntity{Id = userId,LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012")},
//etc.
};
var mock = users.AsQueryable().BuildMockDbSet();

//Aditional setup for FindAsync
mock.Setup(x => x.FindAsync(userId)).ReturnsAsync((object[] ids) =>
{
var id = (Guid)ids[0];
return users.FirstOrDefault(x => x.Id == id);
});
var userRepository = new TestDbSetRepository(mock.Object);

//Execution FindAsync
var user = await ((DbSet<UserEntity>) userRepository.GetQueryable()).FindAsync(userId);
```

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

### Where can I get it?

First, [install NuGet](http://docs.nuget.org/docs/start-here/installing-nuget).

If you are using **Moq** - then, install [MockQueryable.Moq](https://www.nuget.org/packages/MockQueryable.Moq/) from the package manager console:

```
PM> Install-Package MockQueryable.Moq
```

If you are using **NSubstitute** - then, install [MockQueryable.NSubstitute](https://www.nuget.org/packages/MockQueryable.NSubstitute/) from the package manager console:

```
PM> Install-Package MockQueryable.NSubstitute
```

If you are using **FakeItEasy** - then, install [MockQueryable.FakeItEasy](https://www.nuget.org/packages/MockQueryable.FakeItEasy/) from the package manager console:

```
PM> Install-Package MockQueryable.FakeItEasy
```

### Can I use it with my favorite mock framework?

You can install [MockQueryable.EntityFrameworkCore](https://www.nuget.org/packages/MockQueryable.EntityFrameworkCore/) from the package manager console:

```
PM> Install-Package MockQueryable.EntityFrameworkCore
```
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.EntityFrameworkCore.svg)](https://www.nuget.org/packages/MockQueryable.EntityFrameworkCore/)

or even [MockQueryable.Core](https://www.nuget.org/packages/MockQueryable.Core/)
```
PM> Install-Package MockQueryable.Core
```
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.Core.svg)](https://www.nuget.org/packages/MockQueryable.Core/)


Then [make your own extension](https://github.com/romantitov/MockQueryable/blob/master/src/MockQueryable/MockQueryable.Moq/MoqExtensions.cs) for your favorite mock framework

# MockQueryable

[![Build status](https://github.com/romantitov/MockQueryable/workflows/.NET%20Core/badge.svg)](https://github.com/romantitov/MockQueryable/actions)
[![Build status](https://ci.appveyor.com/api/projects/status/ggdbipcyyfb4av9e?svg=true)](https://ci.appveyor.com/project/handybudget/mockqueryable)
[![Build Status](https://travis-ci.org/romantitov/MockQueryable.svg?branch=master)](https://travis-ci.org/romantitov/MockQueryable)
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.Moq.svg)](https://www.nuget.org/packages/MockQueryable.Moq/)
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.NSubstitute.svg)](https://www.nuget.org/packages/MockQueryable.NSubstitute/)
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.FakeItEasy.svg)](https://www.nuget.org/packages/MockQueryable.FakeItEasy/)
[![License](https://img.shields.io/github/license/romantitov/MockQueryable.svg)](https://github.com/romantitov/MockQueryable/blob/master/LICENSE)

[![Build history](https://buildstats.info/appveyor/chart/handybudget/mockqueryable)](https://ci.appveyor.com/project/handybudget/mockqueryable/history)



Extensions for mocking [Entity Framework Core](https://github.com/aspnet/EntityFrameworkCore/) (EFCore) operations such ToListAsync, FirstOrDefaultAsync etc. by [Moq](https://github.com/moq/moq), [NSubstitute](http://nsubstitute.github.io/) or [FakeItEasy](https://fakeiteasy.github.io/)
When writing tests for your application it is often desirable to avoid hitting the database. The extensions allow you to achieve this by creating a context – with behavior defined by your tests – that makes use of in-memory data.

### When should I use it?

If you have something similar to the following code:
```csharp
var query = _userRepository.GetQueryable();

await query.AnyAsync(x =>...)
await query.FirstOrDefaultAsync(x =>...)
query.CountAsync(x => ...)
query.ToListAsync()
//etc.
```
and you want to cover it by unit tests

### How do I get started?

```csharp
//1 - create a List<T> with test items
var users = new List<UserEntity>()
{
new UserEntity{LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012")},
...
};

//2 - build mock by extension
var mock = users.BuildMock();

//3 - setup the mock as Queryable for Moq
_userRepository.Setup(x => x.GetQueryable()).Returns(mock);

//3 - setup the mock as Queryable for NSubstitute
_userRepository.GetQueryable().Returns(mock);

//3 - setup the mock as Queryable for FakeItEasy
A.CallTo(() => userRepository.GetQueryable()).Returns(mock);
```

Do you prefer *DbSet*?

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

//3 - setup DbSet for Moq
var userRepository = new TestDbSetRepository(mock.Object);

//3 - setup DbSet for NSubstitute or FakeItEasy
var userRepository = new TestDbSetRepository(mock);

//3 - setup the mock as Queryable for FakeItEasy
A.CallTo(() => userRepository.GetQueryable()).Returns(mock);
```
### Can I extend the mock object created by MockQueryable with custom logic?
MockQueryable creates for your tests a mock object based on in-memory data, but you can also add some custome logic to it.

``` C#
var userId = Guid.NewGuid();
var users = new List<UserEntity>
{
new UserEntity{Id = userId,LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012")},
//etc.
};
var mock = users.AsQueryable().BuildMockDbSet();

//Aditional setup for FindAsync
mock.Setup(x => x.FindAsync(userId)).ReturnsAsync((object[] ids) =>
{
var id = (Guid)ids[0];
return users.FirstOrDefault(x => x.Id == id);
});
var userRepository = new TestDbSetRepository(mock.Object);

//Execution FindAsync
var user = await ((DbSet<UserEntity>) userRepository.GetQueryable()).FindAsync(userId);
```

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

### Where can I get it?

First, [install NuGet](http://docs.nuget.org/docs/start-here/installing-nuget).

If you are using **Moq** - then, install [MockQueryable.Moq](https://www.nuget.org/packages/MockQueryable.Moq/) from the package manager console:

```
PM> Install-Package MockQueryable.Moq
```

If you are using **NSubstitute** - then, install [MockQueryable.NSubstitute](https://www.nuget.org/packages/MockQueryable.NSubstitute/) from the package manager console:

```
PM> Install-Package MockQueryable.NSubstitute
```

If you are using **FakeItEasy** - then, install [MockQueryable.FakeItEasy](https://www.nuget.org/packages/MockQueryable.FakeItEasy/) from the package manager console:

```
PM> Install-Package MockQueryable.FakeItEasy
```

### Can I use it with my favorite mock framework?

You can install [MockQueryable.EntityFrameworkCore](https://www.nuget.org/packages/MockQueryable.EntityFrameworkCore/) from the package manager console:

```
PM> Install-Package MockQueryable.EntityFrameworkCore
```
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.EntityFrameworkCore.svg)](https://www.nuget.org/packages/MockQueryable.EntityFrameworkCore/)

or even [MockQueryable.Core](https://www.nuget.org/packages/MockQueryable.Core/)
```
PM> Install-Package MockQueryable.Core
```
[![Downloads](https://img.shields.io/nuget/dt/MockQueryable.Core.svg)](https://www.nuget.org/packages/MockQueryable.Core/)


Then [make your own extension](https://github.com/romantitov/MockQueryable/blob/master/src/MockQueryable/MockQueryable.Moq/MoqExtensions.cs) for your favorite mock framework
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,9 @@ namespace MockQueryable.FakeItEasy
{
public static class FakeItEasyExtensions
{
public static IQueryable<TEntity> BuildMock<TEntity>(this IQueryable<TEntity> data) where TEntity : class
public static IQueryable<TEntity> BuildMock<TEntity>(this IEnumerable<TEntity> data) where TEntity : class
{
var mock = A.Fake<IQueryable<TEntity>>(
d => d.Implements<IAsyncEnumerable<TEntity>>().Implements<IQueryable<TEntity>>());
var enumerable = new TestAsyncEnumerableEfCore<TEntity>(data);
((IAsyncEnumerable<TEntity>) mock).ConfigureAsyncEnumerableCalls(enumerable);
mock.ConfigureQueryableCalls(enumerable, data);

return mock;
return new TestAsyncEnumerableEfCore<TEntity>(data);
}

public static DbSet<TEntity> BuildMockDbSet<TEntity>(this IQueryable<TEntity> data) where TEntity : class
Expand Down
8 changes: 2 additions & 6 deletions src/MockQueryable/MockQueryable.Moq/MoqExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ namespace MockQueryable.Moq
{
public static class MoqExtensions
{
public static Mock<IQueryable<TEntity>> BuildMock<TEntity>(this IQueryable<TEntity> data) where TEntity : class
public static IQueryable<TEntity> BuildMock<TEntity>(this IEnumerable<TEntity> data) where TEntity : class
{
var mock = new Mock<IQueryable<TEntity>>();
var enumerable = new TestAsyncEnumerableEfCore<TEntity>(data);
mock.As<IAsyncEnumerable<TEntity>>().ConfigureAsyncEnumerableCalls(enumerable);
mock.ConfigureQueryableCalls(enumerable, data);
return mock;
return new TestAsyncEnumerableEfCore<TEntity>(data);
}

public static Mock<DbSet<TEntity>> BuildMockDbSet<TEntity>(this IQueryable<TEntity> data) where TEntity : class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,9 @@ namespace MockQueryable.NSubstitute
{
public static class NSubstituteExtensions
{
public static IQueryable<TEntity> BuildMock<TEntity>(this IQueryable<TEntity> data) where TEntity : class
public static IQueryable<TEntity> BuildMock<TEntity>(this IEnumerable<TEntity> data) where TEntity : class
{
var mock = Substitute.For<IQueryable<TEntity>, IAsyncEnumerable<TEntity>>();
var enumerable = new TestAsyncEnumerableEfCore<TEntity>(data);
((IAsyncEnumerable<TEntity>) mock).ConfigureAsyncEnumerableCalls(enumerable);
mock.ConfigureQueryableCalls(enumerable, data);
return mock;
return new TestAsyncEnumerableEfCore<TEntity>(data);
}

public static DbSet<TEntity> BuildMockDbSet<TEntity>(this IQueryable<TEntity> data) where TEntity : class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void CreateUserIfNotExist(string firstName, string lastName, DateTime dat
new UserEntity {DateOfBirth = DateTime.Parse("01/20/2012", UsCultureInfo.DateTimeFormat)},
};
//expect
var mock = users.AsQueryable().BuildMock();
var mock = users.BuildMock();
A.CallTo(() => userRepository.GetQueryable()).Returns(mock);
//act
var ex = Assert.ThrowsAsync<ApplicationException>(() =>
Expand All @@ -54,7 +54,7 @@ public async Task GetUserReports(DateTime from, DateTime to, int expectedCount)
var service = new MyService(userRepository);
var users = CreateUserList();
//expect
var mock = users.AsQueryable().BuildMock();
var mock = users.BuildMock();
A.CallTo(() => userRepository.GetQueryable()).Returns(mock);
//act
var result = await service.GetUserReports(from, to);
Expand All @@ -75,7 +75,7 @@ public async Task GetUserReports_AutoMap(DateTime from, DateTime to, int expecte
var service = new MyService(userRepository);
var users = CreateUserList();
//expect
var mock = users.AsQueryable().BuildMock();
var mock = users.BuildMock();
A.CallTo(() => userRepository.GetQueryable()).Returns(mock);
//act
var result = await service.GetUserReportsAutoMap(from, to);
Expand Down
12 changes: 6 additions & 6 deletions src/MockQueryable/MockQueryable.Sample/MyServiceMoqTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public void CreateUserIfNotExist(string firstName, string lastName, DateTime dat
new UserEntity {DateOfBirth = DateTime.Parse("01/20/2012", UsCultureInfo.DateTimeFormat)}
};
//expect
var mock = users.AsQueryable().BuildMock();
userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object);
var mock = users.BuildMock();
userRepository.Setup(x => x.GetQueryable()).Returns(mock);
//act
var ex = Assert.ThrowsAsync<ApplicationException>(() =>
service.CreateUserIfNotExist(firstName, lastName, dateOfBirth));
Expand All @@ -53,8 +53,8 @@ public async Task GetUserReports(DateTime from, DateTime to, int expectedCount)
var service = new MyService(userRepository.Object);
var users = CreateUserList();
//expect
var mock = users.AsQueryable().BuildMock();
userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object);
var mock = users.BuildMock();
userRepository.Setup(x => x.GetQueryable()).Returns(mock);
//act
var result = await service.GetUserReports(from, to);
//assert
Expand All @@ -74,8 +74,8 @@ public async Task GetUserReports_AutoMap(DateTime from, DateTime to, int expecte
var service = new MyService(userRepository.Object);
var users = CreateUserList();
//expect
var mock = users.AsQueryable().BuildMock();
userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object);
var mock = users.BuildMock();
userRepository.Setup(x => x.GetQueryable()).Returns(mock);
//act
var result = await service.GetUserReportsAutoMap(from, to);
//assert
Expand Down
Loading

0 comments on commit 1ea5e1c

Please sign in to comment.