This is a migrated thread and some comments may be shown as answers.

How to Mock DbSet of EF code First ?

5 Answers 655 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Md.Hasanuzzaman
Top achievements
Rank 1
Md.Hasanuzzaman asked on 30 Dec 2013, 06:28 PM
Dear sir,
I am a big fan of telerik control. Recently I have convinced my boss to use use JustMock for my current project. Everything was running smooth and our company was totally impressed with it until I stuck in a problem. I have tried my level best to solve it but can't find a suitable solution. My colleague, Google, Stackoverflow everything failed to satisfy me, so finding no way I came to you with the hope that you would rescue me from this unsolvable problem. I have included my code here, please have a look on it.

Problem : Unable to Mock DbSet 

we are test this Repository but don`t understand How to do it. 

 public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
    {
        protected readonly DbContext DbContext;
        protected DbSet<T> DbSet;

        public BaseRepository(DbContext dbContext)
        {
            DbContext = dbContext;
            DbSet = DbContext.Set<T>();
        }

        public void Add(T entity)
        {
            DbSet.Add(entity);
        }
}

we are tried following code but we are failed.
 
 public class BaseRepositoryTest
    {
        readonly TimeSketchContext _mockDataContext = Mock.Create<TimeSketchContext>();
        private readonly BaseRepository<EmployeeSkill> _repository;
        public BaseRepositoryTest()
        {
            Mock.Arrange(() => _mockDataContext.EmployeeSkill).ReturnsCollection(GetEmployeeSkills());
            _repository = new BaseRepository<EmployeeSkill>(_mockDataContext);
        }

        [Fact]
        public void Add()
        {
            var employeeSkill = GetEmployeeSkill();
            _repository.Add(employeeSkill);
            _mockDataContext.SaveChanges();
            var allSkill = _repository.All();
            Assert.Equal(2, allSkill.Count()); //we are get 0 item here and out test is failed.
        }

        [Fact]
        public void Get_All_Test()
        {
            var allSkill = _repository.All();
            Assert.Equal(1, allSkill.Count()); //we are get 0 item here and out test is failed.

        }


        #region Private Methods
        private EmployeeSkill GetEmployeeSkill()
        {
            return new EmployeeSkill
            {
                SkillDescription = "aa",
                SkillName = "bbbb",
                Id = 1
            };
        }

        private IEnumerable<EmployeeSkill> GetEmployeeSkills()
        {
            return new List<EmployeeSkill>
            {
                GetEmployeeSkill()
            };
        }

        #endregion
    }
if any books on it please refer.

5 Answers, 1 is accepted

Sort by
0
Stefan
Telerik team
answered on 31 Dec 2013, 08:40 AM
Hello Mr. Hasanuzzaman,

Reading through your test, it appears that mock the dependency of BaseRepository<T>, which is a DbContext, but do not arrange all of its methods that the system under test depends on. Whenever you create a mock, you should carefully choose its behavior. In your code you create a recursive loose mock (that's the default behavior of Mock.Create<T>()) - this means that all method calls to the mock will behave like stubs or will return stubs recursively by default. This means, for example, that DbSet = DbContext.Set<T>(); will set DbSet to a stub - it will not set it to _mockDataContext.EmployeeSkill, unless explicitly arranged to do so. This also means that DbSet.Add(entity) will also behave like a stub - in other words it will do nothing (unless explicitly arranged).

When testing without mocks, usually you assert the side effects created by the dependencies of the system under test. This is the same pattern I see in your own tests. When testing with mocks, you should assert the mocks directly. For example, in your code, you test that the BaseRepository.Add method works, by asserting its side effects - namely, that the number of items in the collection, that Add works on, is now 2. However, the test doesn't work because the collection is a stub, not a real collection - calling its Add method simply does nothing. Instead, you should simply assert that the Add method was called at all - if it was called, then, obviously, the code under test behaves correctly. Thus, your test will look like this:
[Fact]
public void Add()
{
    var employeeSkill = GetEmployeeSkill();
    _repository.Add(employeeSkill);
    Mock.Assert(() => _mockDataContext.Set<EmployeeSkill>().Add(employeeSkill), Occurs.Once());
}
In the above method I call the _repository.Add() method and then assert that the .Add method of the underlying DbContext table was called exactly once with the same argument as the one passed to _repository.Add(). This example illustrates the basic test pattern when working with mocks - you don't assert side effects, you assert the mocks directly.

For your second test, you must arrange the respective Set<T> method, instead of the property wrapper. So, your arrange statement should look like this:
Mock.Arrange(() => _mockDataContext.Set<EmployeeSkill>()).ReturnsCollection(GetEmployeeSkills());
This is necessary, because .Set<T> is the method on the mock that BaseRepository uses. It does not use the .EmployeeSkill property directly, so there is no meaning in setting it.

For an introduction to mocking with JustMock I recommend the excellent 30 Days of TDD series.

I hope that my explanation has been helpful. If you require further assistance during your trial, don't hesitate to write to us again. We would be more than happy to assist you in any way we can.

Regards,
Stefan
Telerik
Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
0
Md.Hasanuzzaman
Top achievements
Rank 1
answered on 01 Jan 2014, 04:56 PM
Thank`s  Stefan.
Your are really awesome. Your answered is really helpful.

Now I am in another problem. I am unable to Mock Asynchronous Methods.

Here is my code which I try for :

 My Class :

 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 void Add(T entity)
        {
            InnerDbSet.Add(entity);
        }

        public virtual async Task UpdateAsync(T entity)
        {
            var newEntry = InnerDbContext.Entry(entity);
            if (newEntry.State != EntityState.Detached)
            {
                var oldEntity = await FindAsync(entity.Id);
                if (oldEntity == null)
                    throw new EntityNotFound();
                var attachEntity = InnerDbContext.Entry(oldEntity);
                attachEntity.CurrentValues.SetValues(newEntry);
            }
            else
            {
                newEntry.State = EntityState.Modified;
            }
        }

        public virtual async Task RemoveAsync(T entity)
        {
            entity.IsDelete = true;
            await UpdateAsync(entity);
        }

        public virtual IQueryable<T> All()
        {
            return InnerDbSet.Where(x => !x.IsDelete);
        }

        public virtual Task<T> FindAsync(long id)
        {
            return InnerDbSet.FirstOrDefaultAsync(x => x.Id == id && !x.IsDelete);
        }

        public virtual IQueryable<T> Search(Expression<Func<T, bool>> expression)
        {
            return InnerDbSet.Where(x => x.IsDelete)
                .Where(expression);
        }
    }

My Test :

       readonly TimeSketchContext _mockDataContext = Mock.Create<TimeSketchContext>();
        private readonly BaseRepository<EmployeeSkill> _repository;
        public BaseRepositoryTest()
        {
            _repository = new BaseRepository<EmployeeSkill>(_mockDataContext);
        }

        [Fact]
        public async void Find_Should_Call_Once()
        {
            await _repository.FindAsync(Arg.AnyInt);
            Mock.Assert(() => _mockDataContext.Set<EmployeeSkill>().FirstOrDefaultAsync(x => x.Id == Arg.AnyInt && !x.IsDelete), Occurs.Once());
            // Mock.Assert(() => _mockDataContext.Set<EmployeeSkill>().LastOrDefault(x => x.Id == Arg.AnyInt && !x.IsDelete), Occurs.Once());  test is pass !!!

        }
       above test is also  pass if  change this FirstOrDefaultAsync to LastOrDefault

        [Fact]
        public async void Update_Should_Call_Once()
        {
            var employeeSkill = GetEmployeeSkill();
            employeeSkill.SkillName = "Skill Update";
            await _repository.UpdateAsync(employeeSkill);
            Mock.Assert(() => _repository.FindAsync(Arg.AnyInt), Occurs.Once());
        }
   
       Same Problem.
       
       [Fact]
        public async void Remove_Should_Call_Update()
        {
            await _repository.RemoveAsync(Arg.IsAny<EmployeeSkill>());
            Mock.Assert(() => _repository.UpdateAsync(Arg.IsAny<EmployeeSkill>()), Occurs.Once());
        }

Thank`s in Advance.
0
Kaloyan
Telerik team
answered on 06 Jan 2014, 10:18 AM
Hello Md.Hasanuzzaman,

I have replied in forum thread with ID: 773091. Please, let's continue the discussion there as the issue now differs from the original forum thread title.

Thank you for the understanding.

Regards,
Kaloyan
Telerik
Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
0
Mark
Top achievements
Rank 1
answered on 04 Mar 2015, 01:53 PM
Hi Kaloyan,

I have seen in many places a response stating 'I have replied in forum thread ID :(some number)'.
But I am unable to see how we can find a forum thread by its ID number.
If there is a way could you let me know if not could you provide the title of the forum thread with ID 773091

Kind Regards
Mark
0
Zdravko
Telerik team
answered on 05 Mar 2015, 01:58 PM
Hi Mark,

Usually, the ID works when it comes to ticket thread and I am sure my colleague has provided this reference by accident.
Here is a link to the forum thread.

Regards,
Zdravko
Telerik
 

Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

 
Tags
General Discussions
Asked by
Md.Hasanuzzaman
Top achievements
Rank 1
Answers by
Stefan
Telerik team
Md.Hasanuzzaman
Top achievements
Rank 1
Kaloyan
Telerik team
Mark
Top achievements
Rank 1
Zdravko
Telerik team
Share this question
or