I'm trying to create a unit test for my service with a mocked DbContext. I created an interface IDbContext with the following functions:
public interface IDbContext : IDisposable
{
IDbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
int SaveChanges();
}
My real context implements this interface IDbContext and DbContext.
Now I'm trying to mock the IDbSet<T> in the context, so it returns a List<User> instead.
[TestMethod]
public void TestGetAllUsers()
{
// Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new List<User>
{
new User { ID = 1 }
});
UserService userService = new UserService(mock.Object);
// Act
var allUsers = userService.GetAllUsers();
// Assert
Assert.AreEqual(1, allUsers.Count());
}
I always get this error on .Returns:
The best overloaded method match for
'Moq.Language.IReturns<AuthAPI.Repositories.IDbContext,System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>.Returns(System.Func<System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>)'
has some invalid arguments
I managed to solve it by creating a FakeDbSet<T> class that implements IDbSet<T>
public class FakeDbSet<T> : IDbSet<T> where T : class
{
ObservableCollection<T> _data;
IQueryable _query;
public FakeDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public T Detach(T item)
{
_data.Remove(item);
return item;
}
public T Create()
{
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public ObservableCollection<T> Local
{
get { return _data; }
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
Now my test looks like this:
[TestMethod]
public void TestGetAllUsers()
{
//Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new FakeDbSet<User>
{
new User { ID = 1 }
});
UserService userService = new UserService(mock.Object);
// Act
var allUsers = userService.GetAllUsers();
// Assert
Assert.AreEqual(1, allUsers.Count());
}
In case anyone is still interested, I was having the same problem and found this article very helpful:
Entity Framework Testing with a Mocking Framework (EF6 onwards)
It only applies to Entity Framework 6 or newer, but it covers everything from simple SaveChanges tests to async query testing all using Moq (and a few of manual classes).
Thank you Gaui for your great idea =)
I did add some improvements to your solution and want to share it.
My FakeDbSet also inherents from DbSet to get additional methods
like AddRange()
I replaced the ObservableCollection<T> with List<T> to pass all
the already implemented methods in List<> up to my FakeDbSet
My FakeDbSet:
public class FakeDbSet<T> : DbSet<T>, IDbSet<T> where T : class {
List<T> _data;
public FakeDbSet() {
_data = new List<T>();
}
public override T Find(params object[] keyValues) {
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public override T Add(T item) {
_data.Add(item);
return item;
}
public override T Remove(T item) {
_data.Remove(item);
return item;
}
public override T Attach(T item) {
return null;
}
public T Detach(T item) {
_data.Remove(item);
return item;
}
public override T Create() {
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T {
return Activator.CreateInstance<TDerivedEntity>();
}
public List<T> Local {
get { return _data; }
}
public override IEnumerable<T> AddRange(IEnumerable<T> entities) {
_data.AddRange(entities);
return _data;
}
public override IEnumerable<T> RemoveRange(IEnumerable<T> entities) {
for (int i = entities.Count() - 1; i >= 0; i--) {
T entity = entities.ElementAt(i);
if (_data.Contains(entity)) {
Remove(entity);
}
}
return this;
}
Type IQueryable.ElementType {
get { return _data.AsQueryable().ElementType; }
}
Expression IQueryable.Expression {
get { return _data.AsQueryable().Expression; }
}
IQueryProvider IQueryable.Provider {
get { return _data.AsQueryable().Provider; }
}
IEnumerator IEnumerable.GetEnumerator() {
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator() {
return _data.GetEnumerator();
}
}
It is very easy to modify the dbSet and Mock the EF Context Object:
var userDbSet = new FakeDbSet<User>();
userDbSet.Add(new User());
userDbSet.Add(new User());
var contextMock = new Mock<MySuperCoolDbContext>();
contextMock.Setup(dbContext => dbContext.Users).Returns(userDbSet);
Now it is possible to execute Linq queries, but be a aware that foreign key references may not be created automatically:
var user = contextMock.Object.Users.SingeOrDefault(userItem => userItem.Id == 42);
Because the context object is mocked the Context.SaveChanges() won't do anything and property changes of your entites might not be populated to your dbSet. I solved this by mocking my SetModifed() method to populate the changes.
Based on this MSDN article, I've created my own libraries for mocking DbContext and DbSet:
EntityFrameworkMock - GitHub
EntityFrameworkMockCore - GitHub
Both available on NuGet and GitHub.
The reason I've created these libraries is because I wanted to emulate the SaveChanges behavior, throw a DbUpdateException when inserting models with the same primary key and support multi-column/auto-increment primary keys in the models.
In addition, since both DbSetMock and DbContextMock inherit from Mock<DbSet> and Mock<DbContext>, you can use all features of the Moq framework.
Next to Moq, there also is an NSubstitute implementation.
Usage with the Moq version looks like this:
public class User
{
[Key, Column(Order = 0)]
public Guid Id { get; set; }
public string FullName { get; set; }
}
public class TestDbContext : DbContext
{
public TestDbContext(string connectionString)
: base(connectionString)
{
}
public virtual DbSet<User> Users { get; set; }
}
[TestFixture]
public class MyTests
{
var initialEntities = new[]
{
new User { Id = Guid.NewGuid(), FullName = "Eric Cartoon" },
new User { Id = Guid.NewGuid(), FullName = "Billy Jewel" },
};
var dbContextMock = new DbContextMock<TestDbContext>("fake connectionstring");
var usersDbSetMock = dbContextMock.CreateDbSetMock(x => x.Users, initialEntities);
// Pass dbContextMock.Object to the class/method you want to test
// Query dbContextMock.Object.Users to see if certain users were added or removed
// or use Mock Verify functionality to verify if certain methods were called: usersDbSetMock.Verify(x => x.Add(...), Times.Once);
}
If anyone is still looking for answers I've implemented a small library to allow mocking DbContext.
step 1
Install Coderful.EntityFramework.Testing nuget package:
Install-Package Coderful.EntityFramework.Testing
step 2
Then create a class like this:
internal static class MyMoqUtilities
{
public static MockedDbContext<MyDbContext> MockDbContext(
IList<Contract> contracts = null,
IList<User> users = null)
{
var mockContext = new Mock<MyDbContext>();
// Create the DbSet objects.
var dbSets = new object[]
{
MoqUtilities.MockDbSet(contracts, (objects, contract) => contract.ContractId == (int)objects[0] && contract.AmendmentId == (int)objects[1]),
MoqUtilities.MockDbSet(users, (objects, user) => user.Id == (int)objects[0])
};
return new MockedDbContext<SourcingDbContext>(mockContext, dbSets);
}
}
step 3
Now you can create mocks super easily:
// Create test data.
var contracts = new List<Contract>
{
new Contract("#1"),
new Contract("#2")
};
var users = new List<User>
{
new User("John"),
new User("Jane")
};
// Create DbContext with the predefined test data.
var dbContext = MyMoqUtilities.MockDbContext(
contracts: contracts,
users: users).DbContext.Object;
And then use your mock:
// Create.
var newUser = dbContext.Users.Create();
// Add.
dbContext.Users.Add(newUser);
// Remove.
dbContext.Users.Remove(someUser);
// Query.
var john = dbContext.Users.Where(u => u.Name == "John");
// Save changes won't actually do anything, since all the data is kept in memory.
// This should be ideal for unit-testing purposes.
dbContext.SaveChanges();
Full article: http://www.22bugs.co/post/Mocking-DbContext/
I'm late, but found this article helpful: Testing with InMemory (MSDN Docs).
It explains how to use an in memory DB context (which is not a database) with the benefit of very little coding and the opportunity to actually test your DBContext implementation.
Related
I'm trying to create a unit test for a class that calls into an async repository. I'm using ASP.NET Core and Entity Framework Core. My generic repository looks like this.
public class EntityRepository<TEntity> : IEntityRepository<TEntity> where TEntity : class
{
private readonly SaasDispatcherDbContext _dbContext;
private readonly DbSet<TEntity> _dbSet;
public EntityRepository(SaasDispatcherDbContext dbContext)
{
_dbContext = dbContext;
_dbSet = dbContext.Set<TEntity>();
}
public virtual IQueryable<TEntity> GetAll()
{
return _dbSet;
}
public virtual async Task<TEntity> FindByIdAsync(int id)
{
return await _dbSet.FindAsync(id);
}
public virtual IQueryable<TEntity> FindBy(Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.Where(predicate);
}
public virtual void Add(TEntity entity)
{
_dbSet.Add(entity);
}
public virtual void Delete(TEntity entity)
{
_dbSet.Remove(entity);
}
public virtual void Update(TEntity entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
}
public virtual async Task SaveChangesAsync()
{
await _dbContext.SaveChangesAsync();
}
}
Then I have a service class that calls FindBy and FirstOrDefaultAsync on an instance of the repository:
public async Task<Uri> GetCompanyProductURLAsync(Guid externalCompanyID, string productCode, Guid loginToken)
{
CompanyProductUrl companyProductUrl = await _Repository.FindBy(u => u.Company.ExternalCompanyID == externalCompanyID && u.Product.Code == productCode.Trim()).FirstOrDefaultAsync();
if (companyProductUrl == null)
{
return null;
}
var builder = new UriBuilder(companyProductUrl.Url);
builder.Query = $"-s{loginToken.ToString()}";
return builder.Uri;
}
I'm trying to mock the repository call in my test below:
[Fact]
public async Task GetCompanyProductURLAsync_ReturnsNullForInvalidCompanyProduct()
{
var companyProducts = Enumerable.Empty<CompanyProductUrl>().AsQueryable();
var mockRepository = new Mock<IEntityRepository<CompanyProductUrl>>();
mockRepository.Setup(r => r.FindBy(It.IsAny<Expression<Func<CompanyProductUrl, bool>>>())).Returns(companyProducts);
var service = new CompanyProductService(mockRepository.Object);
var result = await service.GetCompanyProductURLAsync(Guid.NewGuid(), "wot", Guid.NewGuid());
Assert.Null(result);
}
However, when the test executes the call to the repository, I get the following error:
The provider for the source IQueryable doesn't implement IAsyncQueryProvider. Only providers that implement IEntityQueryProvider can be used for Entity Framework asynchronous operations.
How can I properly mock the repository to get this to work?
Thanks to #Nkosi for pointing me to a link with an example of doing the same thing in EF 6: https://msdn.microsoft.com/en-us/library/dn314429.aspx. This didn't work exactly as-is with EF Core, but I was able to start with it and make modifications to get it working. Below are the test classes that I created to "mock" IAsyncQueryProvider:
internal class TestAsyncQueryProvider<TEntity> : IAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal TestAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new TestAsyncEnumerable<TEntity>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
{
return new TestAsyncEnumerable<TResult>(expression);
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
internal class TestAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
{
public TestAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public TestAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IAsyncEnumerator<T> GetEnumerator()
{
return new TestAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IQueryProvider IQueryable.Provider
{
get { return new TestAsyncQueryProvider<T>(this); }
}
}
internal class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public TestAsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public void Dispose()
{
_inner.Dispose();
}
public T Current
{
get
{
return _inner.Current;
}
}
public Task<bool> MoveNext(CancellationToken cancellationToken)
{
return Task.FromResult(_inner.MoveNext());
}
}
And here is my updated test case that uses these classes:
[Fact]
public async Task GetCompanyProductURLAsync_ReturnsNullForInvalidCompanyProduct()
{
var companyProducts = Enumerable.Empty<CompanyProductUrl>().AsQueryable();
var mockSet = new Mock<DbSet<CompanyProductUrl>>();
mockSet.As<IAsyncEnumerable<CompanyProductUrl>>()
.Setup(m => m.GetEnumerator())
.Returns(new TestAsyncEnumerator<CompanyProductUrl>(companyProducts.GetEnumerator()));
mockSet.As<IQueryable<CompanyProductUrl>>()
.Setup(m => m.Provider)
.Returns(new TestAsyncQueryProvider<CompanyProductUrl>(companyProducts.Provider));
mockSet.As<IQueryable<CompanyProductUrl>>().Setup(m => m.Expression).Returns(companyProducts.Expression);
mockSet.As<IQueryable<CompanyProductUrl>>().Setup(m => m.ElementType).Returns(companyProducts.ElementType);
mockSet.As<IQueryable<CompanyProductUrl>>().Setup(m => m.GetEnumerator()).Returns(() => companyProducts.GetEnumerator());
var contextOptions = new DbContextOptions<SaasDispatcherDbContext>();
var mockContext = new Mock<SaasDispatcherDbContext>(contextOptions);
mockContext.Setup(c => c.Set<CompanyProductUrl>()).Returns(mockSet.Object);
var entityRepository = new EntityRepository<CompanyProductUrl>(mockContext.Object);
var service = new CompanyProductService(entityRepository);
var result = await service.GetCompanyProductURLAsync(Guid.NewGuid(), "wot", Guid.NewGuid());
Assert.Null(result);
}
Try to use my Moq/NSubstitute/FakeItEasy extension MockQueryable:
supported all Sync/Async operations (see more examples here)
//1 - create a List<T> with test items
var users = new List<UserEntity>()
{
new UserEntity,
...
};
//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);
DbSet also supported
//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
var userRepository = new TestDbSetRepository(mock);
Notes:
AutoMapper is also supported from 1.0.4 ver
DbQuery supported from 1.1.0 ver
EF Core 3.0 supported from 3.0.0 ver
.Net 5 supported from 5.0.0 ver
Much less code solution. Use the in-memory db context which should take care of bootstrapping all the sets for you. You no longer need to mock out the DbSet on your context but if you want to return data from a service for example, you can simply return the actual set data of the in-memory context.
DbContextOptions< SaasDispatcherDbContext > options = new DbContextOptionsBuilder< SaasDispatcherDbContext >()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
_db = new SaasDispatcherDbContext(optionsBuilder: options);
I'm maintaining two open-source projects that do the heavy lifting of setting up the mocks and actually emulates SaveChanges(Async).
For EF Core: https://github.com/huysentruitw/entity-framework-core-mock
For EF6: https://github.com/huysentruitw/entity-framework-mock
Both projects have Nuget packages with integration for Moq or NSubstitute.
Here is a port of the accepted answer to F#, I just did it for myself and thought it may save someone the time. I have also updated the example to match the updated C#8 IAsyncEnumarable API and tweaked the Mock setup to be generic.
type TestAsyncEnumerator<'T> (inner : IEnumerator<'T> ) =
let inner : IEnumerator<'T> = inner
interface IAsyncEnumerator<'T> with
member this.Current with get() = inner.Current
member this.MoveNextAsync () = ValueTask<bool>(Task.FromResult(inner.MoveNext()))
member this.DisposeAsync () = ValueTask(Task.FromResult(inner.Dispose))
type TestAsyncEnumerable<'T> =
inherit EnumerableQuery<'T>
new (enumerable : IEnumerable<'T>) =
{ inherit EnumerableQuery<'T> (enumerable) }
new (expression : Expression) =
{ inherit EnumerableQuery<'T> (expression) }
interface IAsyncEnumerable<'T> with
member this.GetAsyncEnumerator cancellationToken : IAsyncEnumerator<'T> =
new TestAsyncEnumerator<'T>(this.AsEnumerable().GetEnumerator())
:> IAsyncEnumerator<'T>
interface IQueryable<'T> with
member this.Provider with get() = new TestAsyncQueryProvider<'T>(this) :> IQueryProvider
and
TestAsyncQueryProvider<'TEntity>
(inner : IQueryProvider) =
let inner : IQueryProvider = inner
interface IAsyncQueryProvider with
member this.Execute (expression : Expression) =
inner.Execute expression
member this.Execute<'TResult> (expression : Expression) =
inner.Execute<'TResult> expression
member this.ExecuteAsync<'TResult> ((expression : Expression), cancellationToken) =
inner.Execute<'TResult> expression
member this.CreateQuery (expression : Expression) =
new TestAsyncEnumerable<'TEntity>(expression) :> IQueryable
member this.CreateQuery<'TElement> (expression : Expression) =
new TestAsyncEnumerable<'TElement>(expression) :> IQueryable<'TElement>
let getQueryableMockDbSet<'T when 'T : not struct>
(sourceList : 'T seq) : Mock<DbSet<'T>> =
let queryable = sourceList.AsQueryable();
let dbSet = new Mock<DbSet<'T>>()
dbSet.As<IAsyncEnumerable<'T>>()
.Setup(fun m -> m.GetAsyncEnumerator())
.Returns(TestAsyncEnumerator<'T>(queryable.GetEnumerator())) |> ignore
dbSet.As<IQueryable<'T>>()
.SetupGet(fun m -> m.Provider)
.Returns(TestAsyncQueryProvider<'T>(queryable.Provider)) |> ignore
dbSet.As<IQueryable<'T>>().Setup(fun m -> m.Expression).Returns(queryable.Expression) |> ignore
dbSet.As<IQueryable<'T>>().Setup(fun m -> m.ElementType).Returns(queryable.ElementType) |> ignore
dbSet.As<IQueryable<'T>>().Setup(fun m -> m.GetEnumerator ()).Returns(queryable.GetEnumerator ()) |> ignore
dbSet
A way simpler approach is to write your own ToListAsync in one of the core layers. You dont need any concrete class implementation. Something like:
public static async Task<List<T>> ToListAsync<T>(this IQueryable<T> queryable)
{
if (queryable is EnumerableQuery)
{
return queryable.ToList();
}
return await QueryableExtensions.ToListAsync(queryable);
}
This also has the added benefit that you could use ToListAsync from anywhere in your app without needing to drag EF references all along.
I know this question is old, but I found a nuget package to do this.
MockQueryable
and
MockQueryable.Moq
This does all of the work for you.
[TestCase("AnyFirstName", "AnyExistLastName", "01/20/2012", "Users with DateOfBirth more than limit")]
[TestCase("ExistFirstName", "AnyExistLastName", "02/20/2012", "User with FirstName already exist")]
[TestCase("AnyFirstName", "ExistLastName", "01/20/2012", "User already exist")]
public void CreateUserIfNotExist(string firstName, string lastName, DateTime dateOfBirth, string expectedError)
{
//arrange
var userRepository = new Mock<IUserRepository>();
var service = new MyService(userRepository.Object);
var users = new List<UserEntity>
{
new UserEntity {LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012", UsCultureInfo.DateTimeFormat)},
new UserEntity {FirstName = "ExistFirstName"},
new UserEntity {DateOfBirth = DateTime.Parse("01/20/2012", UsCultureInfo.DateTimeFormat)},
new UserEntity {DateOfBirth = DateTime.Parse("01/20/2012", UsCultureInfo.DateTimeFormat)},
new UserEntity {DateOfBirth = DateTime.Parse("01/20/2012", UsCultureInfo.DateTimeFormat)}
};
//expect
var mock = users.BuildMock();
userRepository.Setup(x => x.GetQueryable()).Returns(mock);
//act
var ex = Assert.ThrowsAsync<ApplicationException>(() =>
service.CreateUserIfNotExist(firstName, lastName, dateOfBirth));
//assert
Assert.AreEqual(expectedError, ex.Message);
}
Leveraging #Jed Veatch's accepted answer, as well as the comments provided by #Mandelbrotter, the following solution works for .NET Core 3.1 and .NET 5. This will resolve the "Argument expression is not valid" exception that arises from working with the above code in later .NET versions.
TL;DR - Complete EnumerableExtensions.cs code is here.
Usage:
public static DbSet<T> GetQueryableAsyncMockDbSet<T>(List<T> sourceList) where T : class
{
var mockAsyncDbSet = sourceList.ToAsyncDbSetMock<T>();
var queryable = sourceList.AsQueryable();
mockAsyncDbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
mockAsyncDbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));
return mockAsyncDbSet.Object;
}
Then, using Moq and Autofixture, you can do:
var myMockData = Fixture.CreateMany<MyMockEntity>();
MyDatabaseContext.SetupGet(x => x.MyDBSet).Returns(GetQueryableAsyncMockDbSet(myMockData));
For everyone who stuck at mocking DbContext with async queries, IAsyncQueryProvider and other things. Heres example usage of copy-paste types for netcore3.1 and higher. Based on generic DbContextCreation and generic DbSet seed.
public class MyDbContext : DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
}
internal class MockDbContextAsynced<TDbContext>
{
private readonly TDbContext _mock;
public TDbContext Object => _mock;
public MockDbContextAsynced()
{
_mock = Activator.CreateInstance<TDbContext>();
}
// suppressed. see full code in source below
}
[Fact]
public void Test()
{
var testData = new List<MyEntity>
{
new MyEntity() { Id = Guid.NewGuid() },
new MyEntity() { Id = Guid.NewGuid() },
new MyEntity() { Id = Guid.NewGuid() },
};
var mockDbContext = new MockDbContextAsynced<MyDbContext>();
mockDbContext.AddDbSetData<MyEntity>(testData.AsQueryable());
mockDbContext.MyEntities.ToArrayAsync();
// or
mockDbContext.MyEntities.SingleAsync();
// or etc.
// To inject MyDbContext as type parameter with mocked data
var mockService = new SomeService(mockDbContext.Object);
}
For full implemented types see this source: https://gist.github.com/Zefirrat/a04658c827ba3ebffe03fda48d53ea11
How to set up where linq extension on object? DbSet in my case. Here is my code:
this.workflowStateSet
.Setup(m => m.Where(It.IsAny<Expression<Func<Model.WorkflowState, int, bool>>>()))
.Returns(new List<Model.WorkflowState>().AsQueryable());
However, it gives me exception not very familiar exception:
System.NotSupportedException: Expression references a method that
does not belong to the mocked object: m => m.Where<WorkflowState>
I will be grateful for any hint.
This extension method will help mock the DbSet
public static class MockDbSetExtensions {
public static Mock<DbSet<T>> AsDbSetMock<T>(this IEnumerable<T> list) where T : class {
IQueryable<T> queryableList = list.AsQueryable();
Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>();
dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider);
dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression);
dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType);
dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator());
return dbSetMock;
}
}
And you can use it like this.
//Arrange
var data = new List<Model.WorkflowState>();
//you would populate your list as needed.
//convert it to a mock DbSet that uses the list as its datasource
var workflowStateSet = data.AsDbSetMock();
var dbSet = workflowStateSet.Object;
//Act
var items = dbSet.Where("Your expression here");
//Assert
//....
Use the repository pattern to add a layer of abstraction to the data retrieval. This abstraction can then be mocked.
If, for example, you were trying to retrieve all of the workflows with a stateId equal to 1, then rather than calling something like this
var result = DbSet.WorkflowState.Where(w => w.stateId == 1);
move this code into another class and then create an interface of the method signature.
public interface IWorkflowStateSetRepository{
IQueryable<Model.WorkflowState> GetAllWorkflows(int state);
}
implementation
public class WorkflowStateSetRepository : IWorkflowStateSetRepository{
public IQueryable<Model.WorkflowState> GetAllWorkflows(int state){
return DbSet.WorkflowState .Where(w => w.stateId == state);
}
}
In the calling code get an instance of IWorkflowStateSetRepository (probably from your IoC container) and call the GetAllWorkflows() method instead. This will give you the same results as before but you can now mock the interface in your tests and setup calls to that methods.
this.MockedIWorkflowStateSetRepository.Setup(m => m.GetAllWorkflows(It.IsAny<int>()))
.Returns(new List<Model.WorkflowState>().AsQueryable());
This code is more maintainable and (with appropriately named variables and methods) also conveys the intent a lot better.
The repository pattern is discussed in greater detail here;
http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Are you trying to Mock up a real DbSet instance ? Cause this won't work, as the error message try to explain you. To mock a type, it must either be an interface or have virtual members (abstract members are also virtual).
You can try to mock up IDbSet or to create a custom DbSet class, for instance something like the following class
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
public class DbSetMock<T> : DbSet<T>, IDbSet<T>
where T : class
{
private readonly ICollection<T> _contentCollection;
public DbSetMock(IList<T> contentCollection = null)
{
_contentCollection = new Collection<T>(contentCollection ?? new List<T>());
AddedEntities = new List<T>();
RemovedEntities = new List<T>();
AttachedEntities = new List<T>();
}
public void OverrideContentCollection(IEnumerable<T> newData)
{
_contentCollection.Clear();
_contentCollection.AddRange(newData);
}
public IList<T> AddedEntities { get; private set; }
public IList<T> AttachedEntities { get; private set; }
public override ObservableCollection<T> Local
{
get
{
throw new NotImplementedException();
}
}
public IList<T> RemovedEntities { get; private set; }
public Type ElementType
{
get
{
return typeof(T);
}
}
public Expression Expression
{
get
{
return _contentCollection.AsQueryable().Expression;
}
}
public IQueryProvider Provider
{
get
{
return _contentCollection.AsQueryable().Provider;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _contentCollection.GetEnumerator();
}
public override T Add(T entity)
{
AddedEntities.Add(entity);
_contentCollection.Add(entity);
return entity;
}
public override T Attach(T entity)
{
AttachedEntities.Add(entity);
var matchingEntity = _contentCollection.SingleOrDefault(x => x.Id == entity.Id);
if (matchingEntity != null)
{
_contentCollection.Remove(matchingEntity);
}
_contentCollection.Add(entity);
return entity;
}
public override TDerivedEntity Create<TDerivedEntity>()
{
throw new NotImplementedException();
}
public override T Create()
{
throw new NotImplementedException();
}
public override T Find(params object[] keyValues)
{
throw new NotImplementedException();
}
public override T Remove(T entity)
{
RemovedEntities.Add(entity);
_contentCollection.Remove(entity);
return entity;
}
}
You can use constructor parameter to setup content that will be retrieved by the db set.
Hope this helps.
public abstract class GenericRepository<T> : IDisposable, IGenericRepository<T> where T : class
{
protected SphereTripMongoDbContext SphereTripMongoDbContext;
public IMongoCollection<T> MongoCollection { get; set; }
protected GenericRepository(SphereTripMongoDbContext sphereTripMongoDbContext)
{
SphereTripMongoDbContext = sphereTripMongoDbContext;
MongoCollection =
SphereTripMongoDbContext.MongoDatabase.GetCollection<T>(typeof(T).Name);
}
public void Dispose()
{
throw new NotImplementedException();
}
public T GetById(string id)
{
var entity = MongoCollection**.Find(t => t.Id == id)**;
return entity;
}
}
I am trying write a generic abstract repository class for MongoDb. Since I am using Generic type in the base class, the "Id" is not visible when I am finding the document using Find method. Not sure how to fix the issue.
Any help would be appreciated.
You can use Find without using a typed lambda expression with Builders:
var item = await collection
.Find(Builders<ItemClass>.Filter.Eq("_id", id))
.FirstOrDefaultAsync();
However, a more robust solution would be to use some interface that gives you what you need (i.e. ID) and making sure GenericRepository only works with these types:
interface IIdentifiable
{
string Id { get; }
}
class GenericRepository <T> : ... where T : IIdentifiable
{
// ...
}
I built a method like this:
public ValueTask<T> GetAsync<T>(IQueryable<T> source, object[] keyValues, CancellationToken cancellationToken = default)
where T : class
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (keyValues == default)
{
throw new ArgumentNullException(nameof(keyValues));
}
if (keyValues.Length != 1)
{
throw new ArgumentException("Key values must contain exactly one key value", nameof(keyValues));
}
var type = typeof(T);
var classMap = BsonClassMap.LookupClassMap(type);
if (classMap == default)
{
throw new InvalidOperationException($"Class map not found for '{type.Name}'");
}
var id = classMap.IdMemberMap;
if (id == default)
{
throw new InvalidOperationException($"Id member not found for '{type.Name}'");
}
var filter = Builders<T>.Filter.Eq(id.ElementName, keyValues[0]);
var collection = Database.GetCollection<T>(type.Name);
async ValueTask<T> GetAsync()
{
var cursor = await collection.FindAsync<T>(filter, default, cancellationToken).ConfigureAwait(false);
return await cursor.SingleOrDefaultAsync(cancellationToken).ConfigureAwait(false);
}
return GetAsync();
}
// acknowledgement: http://stackoverflow.com/a/5022512/1500199
public class FakeDbSet<T> : IDbSet<T> where T : class
{
private readonly HashSet<T> data;
private readonly IQueryable query;
public FakeDbSet()
{
data = new HashSet<T>();
query = data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException();
}
}
How can I implement Find?
I need to determine the primary key value of T in order to perform a key value comparison in the Find method but I do not know how.
Try this:
public class FakeDbSet<T> : IDbSet<T> where T : class
{
private Func<T, object[], bool> _findSelector
private readonly HashSet<T> data;
private readonly IQueryable query;
public FakeDbSet(Func<T, object[], bool> findSelector)
{
_findSelector = findSelector;
data = new HashSet<T>();
query = data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
return _data.SingleOrDefault(item => _findSelector(item, keyValues));
}
}
If I understood you properly You need to stub the method Find() of the DbSet object for unit testing purposes.
If you are using EntityFramework, which has version 6 or higher and using Mocking Frameworks such as Moq or NSubstitute or FakeItEasy you can achieve this easily without writing classes like your FakeDbSet.
This article provides full explanation of stubbing/mocking process: http://msdn.microsoft.com/en-us/data/dn314429.aspx
And an example using classes from this article.
[Test]
public void Foo_WhenCalled_CallsDb()
{
//Arrange
var fakeDbSet = new Mock<DbSet<Course>>();
fakeDbSet.Setup(dbs => dbs.Find(It.IsAny<object>())).Returns(new Course { CourseID = 125 });
var fakeContext = new Mock<SchoolEntities>();
fakeContext.Setup(c => c.Courses).Returns(fakeDbSet.Object);
var foo = new Foo(fakeContext.Object);
//Act
string result = foo.MyMethod();
//Assert
Assert.AreEqual("125", result);
}
For other versions of EntityFramework consider using "Repository" pattern - it provides great testing capabilities.
I hope my answer will help you.
Override the method
public class FakePostsDbSet : FakeDbSet<Post>
{
public override Post Find(params object[] keyValues)
{
return this.SingleOrDefault(
post => post.Slug == (string) keyValues.Single());
}
}
I have the following class:
public class InMemoryRepository : IRepository
{
public void Add(object entity)
{
throw new NotImplementedException();
}
public void Attach(object Entity)
{
throw new NotImplementedException();
}
public T Get<T>(object id)
{
throw new NotImplementedException();
}
public IList<T> GetAll<T>(string queryName)
{
throw new NotImplementedException();
}
public IList<T> GetAll<T>()
{
throw new NotImplementedException();
}
public IQueryable<T> Query<T>()
{
throw new NotImplementedException();
}
public void Remove(object entity)
{
throw new NotImplementedException();
}
public void Save(object entity)
{
throw new NotImplementedException();
}
}
Our default repository implementation uses NHibernate for the backing store, but I'd like to implement an in-memory version of it so I can prototype the domain objects without having to create a backing SQL database. Assuming the convention that all objects have an Id property as the primary key, how would you implement a generic memory store for this?
Some key points I'm having a hard time addressing:
The repository methods themselves are generic, so I need some mechanism for automatically storing and referencing different types. Get<TestEntity>(object id) should be able to query all stored instances of TestEntity and find the one with the matching Id property, but I can't define a collection of TestEntity objects directly, as the repository won't know what types I'm feeding it until runtime.
I need to support LINQ to Objects for the Query() method. Assuming I can come up with a decent way to store the objects, this should be as simple as returning an array of stored objects AsQueryable().
How would you store the objects to meet the above requirements?
Basics are simple:
public class InMemoryRepository : IRepository
{
private readonly IList<object> entities = new List<object>();
public T Get<T>(object id)
{
return entities.OfType<T>.SingleOrDefault(e => e.ID == id);
}
public IList<T> GetAll<T>()
{
return entities.OfType<T>.ToList();
}
public IQueryable<T> Query<T>()
{
return GetAll<T>.AsQueryable();
}
}
However, as soon as it comes to public IList<T> GetAll<T>(string queryName), things get complicated.
Potentially you can resort to an SQLite-based repository implementation for your tests.
I would go with NHibernate configured for in-memory SqlLite database. You can test then your real code and be sure that everything works correct. Writing mock for Repository can be hard and if you change IRepository interface you will have to reimplement you InMemoryRepository.
For me one of big benefits of having NHibernate is the possibility for using in memory database for testing.
With Anton's answer I was able to fix my own InMemoryRepository. I have modified it to match the class in the question:
private readonly ConcurrentDictionary<Type, List<object>> ObjectList = new ConcurrentDictionary<Type, List<object>>();
public int Add<T>(T obj) where T : IIdentifier
{
// instantiate if list does not exist for this object type
if (!ObjectList.ContainsKey(typeof (T)))
ObjectList[typeof(T)] = new List<object>();
// get id
var id = GetId<T>() + 1;
// add object to list
obj.Id = id;
ObjectList[typeof(T)].Add(obj);
return id;
}
public void Attach<T>(T obj) {
// do not need to do anything
}
public T Get<T>(int id) where T : class, IIdentifier
{
// check list exist
if (!ObjectList.ContainsKey(typeof (T)))
return null;
return ObjectList[typeof(T)].OfType<T>().FirstOrDefault(n => n.Id == id);
}
public List<T> GetAll<T>(Func<T, bool> predicate) where T : new()
{
// check list exist
if (!ObjectList.ContainsKey(typeof(T)))
return null;
return ObjectList[typeof(T)].OfType<T>().Where(predicate).ToList();
}
public List<T> GetAll<T>()
{
return ObjectList[typeof(T)].OfType<T>.ToList();
}
public IQueryable<T> Query<T>()
{
return GetAll<T>.AsQueryable();
}
public int Remove<T>(int id) where T : IIdentifier
{
// check list exist
if (!ObjectList.ContainsKey(typeof(T)))
return 0;
// find object with matching id
for (var i = 0; i < ObjectList[typeof(T)].Count; i++)
if (ObjectList[typeof(T)].OfType<T>().ToList()[i].Id == id)
{
ObjectList[typeof(T)].RemoveAt(i);
return id;
}
// object not found
return 0;
}
public int Save<T>(T obj) where T : IIdentifier
{
// check list exist
if (!ObjectList.ContainsKey(typeof(T)))
return 0;
// find object with matching id
for (var i = 0; i < ObjectList[typeof(T)].Count; i++)
if (ObjectList[typeof(T)].OfType<T>().ToList()[i].Id == obj.Id)
{
ObjectList[typeof (T)][i] = obj;
return obj.Id;
}
// object not found
return 0;
}
#region Helper methods
private int GetId<T>() where T : IIdentifier
{
return ObjectList[typeof(T)].Count == 0 ? 0 : ObjectList[typeof(T)].OfType<T>().Last().Id;
}
#endregion
Here is an implementation of fake repository based on DbSet, including find by primary keys:
http://refactorthis.wordpress.com/2011/11/30/generic-repository-fake-idbset-implementation-update-find-method-identity-key/