I am new in mocking. I want to mock up my base repository which is depend on Entity Framework 6 DbContext But I fail. I searched in Google a lot but did not get any sufficient result. At last I got an example at testing with async queries and try to follow but it is worked for me.
Here is my code :
DbContext :
public class TimeSketchContext : DbContext
{
public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}
Base Repository :
public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
{
protected readonly DbContext InnerDbContext;
protected DbSet<T> InnerDbSet;
public BaseRepository(DbContext innerDbContext)
{
InnerDbContext = innerDbContext;
InnerDbSet = InnerDbContext.Set<T>();
}
public virtual Task<T> FindAsync(long id)
{
return InnerDbSet.FirstOrDefaultAsync(x=>x.Id == id);
}
}
Test :
[Fact]
public async Task DbTest()
{
var dummyData = GetEmployeeSkills();
var mockSet = new Mock<DbSet<EmployeeSkill>>();
mockSet.As<IDbAsyncEnumerable<EmployeeSkill>>()
.Setup(x => x.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<EmployeeSkill>(dummyData.GetEnumerator()));
mockSet.As<IQueryable<EmployeeSkill>>()
.Setup(x => x.Provider)
.Returns(new TestDbAsyncQueryProvider<EmployeeSkill>(dummyData.Provider));
mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.Expression).Returns(dummyData.Expression);
mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.ElementType).Returns(dummyData.ElementType);
mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.GetEnumerator()).Returns(dummyData.GetEnumerator());
var mockContext = new Mock<TimeSketchContext>();
mockContext.Setup(c => c.EmployeeSkill).Returns(mockSet.Object);
var baseRepository = new BaseRepository<EmployeeSkill>(mockContext.Object);
var data = await baseRepository.FindAsync(1);
Assert.NotEqual(null, data);
}
private EmployeeSkill GetEmployeeSkill()
{
return new EmployeeSkill
{
SkillDescription = "SkillDescription",
SkillName = "SkillName",
Id = 1
};
}
private IQueryable<EmployeeSkill> GetEmployeeSkills()
{
return new List<EmployeeSkill>
{
GetEmployeeSkill(),
GetEmployeeSkill(),
GetEmployeeSkill(),
}.AsQueryable();
}
Result is :
Assert.NotEqual() Failure
I think problem is
public BaseRepository(DbContext innerDbContext)
{
InnerDbContext = innerDbContext;
InnerDbSet = InnerDbContext.Set<T>(); <<<<<<<<<<<
}
But don`t understand why and how to solve this.
I am using :
Visual Studio 2013 Ultimate
Moq
xUnit
Thank`s in advance.
You are right the problem is in your InnerDbContext.Set<T>(); statement.
In the current version of the EF (6.0.2) the DbContext.Set<T> method is not virtual so it cannot be mocked with Moq.
So you cannot easily make your test pass except by changing your design of the BaseRepository to not depend on the whole DbContext but on one DbSet<T>:
So something like:
public BaseRepository(DbSet<T> dbSet)
{
InnerDbSet = dbSet;
}
Then you can pass directly in your mocked DbSet.
Or you can create a wrapper interface for DbContext:
public interface IDbContext
{
DbSet<T> Set<T>() where T : class;
}
public class TimeSketchContext : DbContext, IDbContext
{
public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}
Then use IDbContext in your BaseRepository:
public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
{
protected readonly IDbContext InnerDbContext;
protected DbSet<T> InnerDbSet;
public BaseRepository(IDbContext innerDbContext)
{
InnerDbContext = innerDbContext;
InnerDbSet = InnerDbContext.Set<T>();
}
public virtual Task<T> FindAsync(long id)
{
return InnerDbSet.FirstOrDefaultAsync(x => x.Id == id);
}
}
And finally you just need to change two lines in your test to make it pass:
var mockContext = new Mock<IDbContext>();
mockContext.Setup(c => c.Set<EmployeeSkill>()).Returns(mockSet.Object);
Related
In my ASP.NET Core 6 Web API project, I am implementing Repository and UnitOfWork:
I have this code:
IMerchantRepository:
public interface IAdminMerchantRepository : IGenericRepository<Merchant>
{
IQueryable<AllMerchantListDto> GetAllMerchantAsync(PagingFilter filter);
}
MerchantRepository:
public class AdminMerchantRepository : GenericRepository<Merchant>, IAdminMerchantRepository
{
private readonly ApplicationDbContext _dbContext;
private readonly DbSet<Merchant> _adminMerchants;
public AdminMerchantRepository(ApplicationDbContext dbContext) : base(dbContext)
{
_dbContext = dbContext;
_adminMerchants = _dbContext.Set<Merchant>();
}
public IQueryable<AllMerchantListDto> GetAllMerchantAsync(PagingFilter filter)
{
var merchants = _dbContext.Merchants
.Where(x => string.IsNullOrEmpty(filter.SearchQuery) || x.User.UserName.ToLower().Contains(filter.SearchQuery.ToLower())
|| x.User.Email.ToLower().Contains(filter.SearchQuery.ToLower())
|| x.User.FirstName.ToLower().Contains(filter.SearchQuery.ToLower())
|| x.User.LastName.ToLower().Contains(filter.SearchQuery.ToLower())
|| x.MerchantName.ToLower().Contains(filter.SearchQuery.ToLower()))
.Include(x => x.User)
.OrderByDescending(x => x.CreatedAt);
return (IQueryable<AllMerchantListDto>)merchants;
}
}
IUnitOfWork:
public interface IUnitOfWork : IDisposable
{
IAdminMerchantRepository AdminMerchants { get; }
Task Save();
}
UnitOfWork:
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _dbContext;
private IAdminMerchantRepository _adminMerchants;
public UnitOfWork(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public IAdminMerchantRepository AdminMerchants => _adminMerchants ??= new AdminMerchantRepository(_dbContext);
public async Task Save()
{
await _dbContext.SaveChangesAsync();
}
public void Dispose()
{
_dbContext.Dispose();
GC.SuppressFinalize(this);
}
}
I got this error:
Non-invocable member 'IUnitOfWork.AdminMerchants' cannot be used like a method
Then this AdminMerchants highlighted in:
var merchant = await _unitOfWork.AdminMerchants(filter);
It look like you're calling
var merchant = await _unitOfWork.AdminMerchants(filter);
where you mean to be calling
var merchant = await _unitOfWork.AdminMerchants.GetAllMerchantAsync(filter);.
GetAllMerchantAsync is not async method. You need call await _unitOfWork.AdminMerchants.GetAllMerchantAsync(filter).ToListAsync() and better to rename method to GetAllMerchant.
Your query will fail, because you have produced not IQueryable<AllMerchantListDto> but IQueryable<Merchant>. That's why you have applied wrong explicit cast.
It should be:
return merchants.Select(m => new AllMerchantListDto
{
... // assign properties
});
Do not create additional abstractions if they are not needed. DbContext is already Unit Of Work and DbSet is already Repository. GetAllMerchant can be just extension method and no additional abstractions are needed.
I got the following error when I try to test an update operation using Entity Framework core:
System.InvalidOperationException : The instance of entity type 'Companies' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
After doing some research, I tried everything that I found:
Create in scope DB context
deattach and attached the object I want to update from the DB context
Return the object to be updated using "AsNoTracking()" , my repository actually do this.
For the testing I am using EF in-memmory database with it fixture, I am using XUnit and .NET 5.
Can I get any help with this please?
Here is my code:
// The repository I am trying to test
public class RepositoryBase<T> : ICrudRepository<T> where T : class, IModel
{
protected PrjDbContext DatabaseContext { get; set; }
public RepositoryBase(PrjDbContext databaseContext) => DatabaseContext = databaseContext;
protected IQueryable<T> FindAll() => DatabaseContext.Set<T>().AsNoTracking();
protected IQueryable<T> FindBy(Expression<Func<T, bool>> expression) => DatabaseContext.Set<T>().Where(expression).AsNoTracking();
public void Create(T entity) => DatabaseContext.Set<T>().Add(entity);
public void Update(T entity) => DatabaseContext.Set<T>().Update(entity);
public void Delete(T entity) => DatabaseContext.Set<T>().Remove(entity);
public async Task<IEnumerable<T>> ReadAllAsync() => await FindAll().ToListAsync().ConfigureAwait(false);
public async Task<T> ReadByIdAsync(int id) => await FindBy(entity => entity.Id.Equals(id)).FirstOrDefaultAsync().ConfigureAwait(false);
}
//The Database context
public partial class PrjDbContext : DbContext
{
public PrjDbContext()
{
}
public PrjDbContext(DbContextOptions<PrjDbContext> options)
: base(options)
{
}
public virtual DbSet<Companies> Companies { get; set; }
}
// This is my fixture with the in-memory Database
public sealed class PrjSeedDataFixture : IDisposable
{
public PrjDbContext DbContext { get; }
public PrjSeedDataFixture(string name)
{
string databaseName = "PrjDatabase_" + name + "_" + DateTime.Now.ToFileTimeUtc();
DbContextOptions<PrjDbContext> options = new DbContextOptionsBuilder<PrjDbContext>()
.UseInMemoryDatabase(databaseName)
.EnableSensitiveDataLogging()
.Options;
DbContext = new PrjDbContext(options);
// Load Companies
DbContext.Companies.Add(new Companies { Id = 1, Name = "Customer 1", Status = 0, Created = DateTime.Now, LogoName = "FakeLogo.jpg", LogoPath = "/LogoPath/SecondFolder/", ModifiedBy = "Admin" });
DbContext.Companies.AsNoTracking();
DbContext.SaveChanges();
}
public void Dispose()
{
DbContext.Dispose();
}
}
The test method "Update_WhenCalled_UpdateACompanyObject", is not working for me.
// And finally, this is my test class, Create_WhenCalled_CreatesNewCompanyObject pass the test, but Update_WhenCalled_UpdateACompanyObject isn't passing the test.
public class RepositoryBaseCompanyTests
{
private Companies _newCompany;
private PrjDbContext _databaseContext;
private RepositoryBase<Companies> _sut;
public RepositoryBaseCompanyTests()
{
_newCompany = new Companies {Id = 2};
_databaseContext = new PrjSeedDataFixture("RepositoryBase").DbContext;
_sut = new RepositoryBase<Companies>(_databaseContext);
}
[Fact]
public void Create_WhenCalled_CreatesNewCompanyObject()
{
//Act
_sut.Create(_newCompany);
_databaseContext.SaveChanges();
//Assert
Assert.Equal(2, _databaseContext.Companies.Where( x => x.Id == 2).FirstOrDefault().Id);
}
[Fact]
public async void Update_WhenCalled_UpdateACompanyObject()
{
//Arrange
var company = await _sut.ReadByIdAsync(1);
company.Name = "Customer 2";
//_databaseContext.Entry(company).State = EntityState.Detached;
//_databaseContext.Attach(company);
//_databaseContext.Entry(company).State = EntityState.Modified;
//Act
_sut.Update(company);
await _databaseContext.SaveChangesAsync();
//Assert
Assert.Equal("Customer 2", _databaseContext.Companies.Where(x => x.Id == 1).FirstOrDefault().Name);
}
}
If you are using EF Core 5.0 then call DbContext.ChangeTracker.Clear() (or go through DbContext.Entries collection and set state to Detached for earlier ones) after DbContext.SaveChanges(); in PrjSeedDataFixture ctor. Adding/Updating an entry makes it tracked and you are reusing the context that created an entry with Id = 1, so when _sut.Update(company); is called it will try to track it again (since ReadByIdAsync should return an untracked one).
P.S.
Adding an extra repository abstraction layer around EF can be considered as antipattern (because EF already implements repository/UoW patterns) and the issue you are having can be one of the examples of why that is true and why this abstraction can be a leaky one. So if you still decide that having one is a good idea - you need to proceed with caution.
I am trying to setup tests using Moq + Nunit.
I am using the Repository pattern
public interface IRepository<T> where T : class
{
Task<List<T>> FindByConditionAsync(Expression<Func<T, bool>> expression);
Task<List<T>> GetAllAsync();
//...
}
public class Repository<TEntity, TContext> : IRepository<TEntity>
{
//...Omitted other code from this snippet
public async Task<List<TEntity>> FindByConditionAsync(Expression<Func<TEntity, bool>> expression)
{
return await _context.Set<TEntity>().Where(expression).ToListAsync();
}
}
public interface IUserRepository : IRepository<User>
{
}
public class UserRepository : Repository<User, MyDatabase>, IUserRepository
{
public UserRepository(MyDatabase context) : base(context)
{
}
}
My test file
public class UserProviderTests
{
private IMapper _mapper;
private UserProvider _userProvider;
private Mock<MyDatabase> _mockContext;
private UserRepository _userRepository;
[SetUp]
public void Setup()
{
var user1Guid = Guid.NewGuid();
var user2Guid = Guid.NewGuid();
var data = new List<User>
{
new User
{
Id = user1Guid,
FirstName = "Optimus",
LastName = "Prime"
},
new User
{
Id = user2Guid,
FirstName = "John",
LastName = "Doe",
}
}.AsQueryable();
var mockSet = new Mock<DbSet<User>>();
mockSet.As<IQueryable<User>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<User>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var options = new DbContextOptionsBuilder<MyDatabase>()
.UseSqlServer("fakestring")
.Options;
_mockContext = new Mock<MyDatabase>(options);
_mockContext.Setup(c => c.Users).Returns(mockSet.Object);
_userRepository = new UserRepository(_mockContext.Object);
var config = new MapperConfiguration(cfg => {
cfg.AddProfile<MappingProfile>();
});
_mapper = new Mapper(config);
_userProvider = new UserProvider(_mapper, _userRepository);
}
[Test]
public async Task GetUsers_ReturnsSingleUser()
{
var users = await _userProvider.FindUser("xxx");
Assert.AreEqual("xxx", users.FirstOrDefault()?.FirstName);
}
}
UserProvider is a service I am using that uses UserRepository to perform CRUD operations.
But When I try to run this test, I get error saying -
System.ArgumentNullException : Value cannot be null. (Parameter 'source')
Stack Trace:
Queryable.Where[TSource](IQueryable'1 source, Expression'1 predicate)
Repository'2.FindByConditionAsync(Expression'1 expression) line 48
Sorry for the long question, but am I missing something here? I don't understand why TSource(USer object) is null.
In my opinion, you should write a test for services in your project. In fact, you shouldn't write any business in the RepositoryLayer because there isn't any business rule in the Repository. If you need to help more please contact me.
dpournabi#gmail.com
I'm trying to test my EF repository with MOQ.
My context:
public class PhotoDbContext : DbContext, IDbContext
{
public PhotoDbContext(){} // to enable data migrations
public PhotoDbContext(string connectionString, bool debugOutput = false) : base(connectionString)
{
if (debugOutput)
// Write to output all sql operations
Database.Log = s => Debug.WriteLine(s);
}
public virtual new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
public virtual new void SaveChanges()
{
base.SaveChanges();
}
public new DbEntityEntry Entry<TEntity>(TEntity entity) where TEntity : class
{
return base.Entry(entity);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Configure(modelBuilder);
}
public new void Dispose()
{
base.Dispose();
}
public new DbContextConfiguration Configuration
{
get { return base.Configuration; }
}
My repo:
public class PhotoRepository : IPhotoRepository
{
private readonly IDbContext _context;
public PhotoRepository(IDbContext context)
{
_context = context;
}
public void Save(Photo album)
{
_context.Set<Photo>().Add(album);
_context.SaveChanges();
}
public void Update(Photo entity)
{
_context.Set<Photo>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified; **// null ref exception**
_context.SaveChanges();
}
And finally the test im trying to run.
[TestMethod]
public void UpdatePhoto()
{
var mockSet = new Mock<DbSet<Photo>>();
var mockContext = new Mock<PhotoDbContext>();
mockContext.Setup(m => m.Set<Photo>()).Returns(mockSet.Object);
var service = new PhotoRepository(mockContext.Object);
var photo = new Photo
{
Name = "123",
Order = 2
};
service.Save(photo);
mockSet.Verify(m => m.Add(It.IsAny<Photo>()), Times.Once);
mockContext.Verify(m => m.SaveChanges(), Times.Once);
photo.Name = "updated";
service.Update(photo);
mockSet.Verify(m => m.Add(It.IsAny<Photo>()), Times.Exactly(2));
mockContext.Verify(m => m.SaveChanges(), Times.Exactly(2));
}
The problem is I am getting a Null reference exception in repositories update method where I try to set Entry.State = Entity.Modified
I tried adding level of indirection in my context but it doesn't work.
What am I missing? Any help would be appreciated.
The _context object is your mock object mockContext and the part that is returning null is _context.Entry(entity) because you haven't set this up to return anything within the test.
If you add something like this with the object someObjectThatYouHaveSetup being whatever you want to return from _context.Entry(entity), it should work.
mockContext.Setup(x => x.Entry(photo)).Returns(someObjectThatYouHaveSetup)
As an answer to my own question:
What would be the most elegant way to use Entity Framework with Generic Repository, in Service Stack, and to write Integration \ Unit Tests for service?
At the moment, this is how my structure looks like:
Generic repository layer:
public class GenericRepository<TEntity> where TEntity : class
{
internal DbContext Context;
//...
//CRUD Operations, etc.
}
Unit of work layer:
public class UnitOfWork : IDisposable
{
private readonly DbContext _context;
public UnitOfWork(DbContext ctx)
{
_context = ctx;
}
private bool _disposed;
private GenericRepository<User> _userRepository;
public GenericRepository<User> UserRepository
{
get { return _userRepository ?? (_userRepository = new GenericRepository<User>(_context)); }
}
//...
}
Business layer:
public class UserBusiness
{
public UnitOfWork UoW { get; set; }
public void AddUser(Models.User user)
{
//Map from domain model to entity model
var u = Mapper.Map<Models.User, DAL.Repository.User>(user);
UoW.UserRepository.Insert(u);
UoW.Save();
}
}
API project:
public class AppHost : AppHostBase
{
public AppHost() : base("Users Service", typeof(UsersService).Assembly) { }
//...
public override void Configure(Funq.Container container)
{
//...other configuration
//UoW registration
container.Register(c => new UnitOfWork(new DbContext("my-DB-connection"))).ReusedWithin(Funq.ReuseScope.Hierarchy);
//Business layer class registration
container.Register<UserBusiness>(c=>new UserBusiness {
UoW = c.Resolve<UnitOfWork>()
}).ReuseWithin(Funq.ReuseScope.Hierarchy);
}
}
public class UsersService : Service
{
public UserBusiness UB { get; set; }
public object Post(User u)
{
UB.AddUser(u);
//...
}
}
So when it comes to integration testing, I can just do something like this:
Declare _appHost
private ServiceStackHost _appHost;
And configure funq container like this:
public override void Configure(Funq.Container container)
{
//...create mocked context
var mockedContext = new Mock<IDbContext>();
mockedContext.Setup(x => x.Set<User>()).Returns(new List<User>
{
new User { ID = 1, FirstName = "John", LastName = "Doe" }
});
//(or use effort or any other way to create it)
container.Register(c => new UnitOfWork(mockedContext)).ReusedWithin(Funq.ReuseScope.Hierarchy);
}
And test as usual:
[Test]
public void Get_User_By_Id()
{
//...generate client instance (JsonServiceClient) etc.
var customer = client.Get(new GetCustomer { Id = 1 });
Assert.AreEqual("John", customer.FirstName);
///...
}
In addition to have all layers available for DI, and mocking, I also created IDbContext, IGenericRepository, IUnitOfWork, etc. interfaces.
I didn't include it here in order to keep this as simple as I could.
But I would like to hear if there's a better (more elegant way) to do it.