开发者

Cannot seem to moq EF CodeFirst 4.1.Help anyone?

开发者 https://www.devze.com 2023-02-28 14:40 出处:网络
I have been given the task to evaluate codeFirst and possible to use for all our future projects. The evaluation is based on using codeFirst with an existing database.

I have been given the task to evaluate codeFirst and possible to use for all our future projects. The evaluation is based on using codeFirst with an existing database.

Wondering if it's possible to mock the repository using codeFirst 4.1.(no fakes)

The idea is to inject a repository into a service and moq the repository.

I have been looking on the net but I have only found an example using fakes.I dont want to use fakes I want to use moq.

I think my problem is in the architecture of the DAL.(I would like to use unitOfWork etc.. by I need to show a working moq example)

Below is my attempt(Failed miserably) due to lack of knowledge on Code first 4.1. I have also uploaded a solution just in case somebody is in good mood and would like to change it.

http://cid-9db5ae91a2948485.office.live.com/browse.aspx/Public%20Folder?uc=1

I am open to suggestions and total modification to my Dal.Ideally using Unity etc.. but I will worry about later. Most importantly I need to be able to mock it. Without ability to use MOQ we will bin the project using EF 4.1

Failed attempt

//CodeFirst.Tests Project
[TestClass]
public class StudentTests
{
    [TestMethod]
    public void Should_be_able_to_verify_that_get_all_has_been_called()
    {
        //todo redo test once i can make a simple one work
        //Arrange
        var repository = new Mock<IStudentRepository>();
        var expectedStudents = new List<Student>();
        repository.Setup(x => x.GetAll()).Returns(expectedStudents);

        //act
        var studentService = new StudentService(repository.Object);
        studentService.GetAll();

        //assert
        repository.Verify(x => x.GetAll(), Times.AtLeastOnce());
    }

}

//CodeFirst.Common Project
public class Student
{
    public int StudentId { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
}
public interface IStudentService
{
    IEnumerable<Student> GetAll();
}

//CodeFirst.Service Project
public class StudentService:IStudentService
{
    private IStudentRepository _studentRepository;

    public StudentService()
    {
    }

    public StudentService(IStudentRepository studentRepository)
    {
        _studentRepository = studentRepository;
    }


    public IEnumerable<Student> GetAll()
    {
        //TODO when mocking using moq this will actually call the db as we need a separate class.
        using (var ctx = new SchoolContext("SchoolDB"))
        {
            _studentRepository = new StudentRepository(ctx);
            var students = _studentRepository.GetAll().ToList();
            return students;
        } 
    }
}

//CodeFirst.Dal Project
public interface IRepository<T> where T : class
{
    T GetOne(Expression<Func<T, bool>> predicate);
    IEnumerable<T> GetAll();
    IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
    void Add(T entity);
    void Delete(T entity);
    T Single(Func<T, bool> predicate);
    T First(Func<T, bool> predicate);
}
public class RepositoryBase<T> : IRepository<T> where T : class
{
    private readonly IDbSet<T> _dbSet;

    public RepositoryBase(DbContext dbContext)
    {
        _dbSet = dbContext.Set<T>();
        if (_dbSet == null) throw new InvalidOpe开发者_如何学JAVArationException("Cannot create dbSet ");
    }

    protected virtual IDbSet<T> Query
    {
        get { return _dbSet; }
    }

    public T GetOne(Expression<Func<T, bool>> predicate)
    {
        return Query.Where(predicate).FirstOrDefault();
    }

    public IEnumerable<T> GetAll()
    {
        return Query.ToArray();
    }

    public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return Query.Where(predicate).ToArray();
    }

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

    public void Delete(T entity)
    {
        _dbSet.Remove(entity);
    }


    public T Single(Func<T, bool> predicate)
    {
        return Query.Where(predicate).SingleOrDefault();
    }

    public T First(Func<T, bool> predicate)
    {
        return Query.Where(predicate).FirstOrDefault();
    }

}
 public class SchoolContext:DbContext
{
    public SchoolContext(string connectionString):base(connectionString)
    {
        Database.SetInitializer<SchoolContext>(null);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Not sure why I have to do this.Without this when using integration testing
        //as opposed to UnitTests it does not work.
        modelBuilder.Entity<Student>().ToTable("Student");       }


    public DbSet<Student> Students { get; set; }
}
public interface IStudentRepository:IRepository<Student>
{

}
public class StudentRepository : RepositoryBase<Student>, IStudentRepository
{
    public StudentRepository(DbContext dbContext)
        : base(dbContext)
    {
    }

    public IEnumerable<Student> GetStudents()
    {
        return GetAll();
    }
}

Again feel free to modify or whatever is needed to help me to get something together.

Thanks a lot for your help


When I started with repository and unit of work patterns I used the implementation similar to this (it is for ObjectContext API but converting it to DbContext API is simple). We used that implementation with MOQ and Unity without any problems. By the time implementations of repository and unit of work have evolve as well as the approach of injecting. Later on we found that whole this approach has serious pitfalls but that was alredy discussed in other questions I referenced here (I highly recommend you to go through these links).

It is very surprising that you are evaluating the EFv4.1 with high emphasis on mocking and unit testing and in the same time you defined service method which is not unit-testable (with mocking) at all. The main problem of you service method is that you are not passing repository/context as dependency and because of that you can't mock it. The only way to test your service and don't use the real repository is using some very advanced approach = replacing mocking and MOQ with detouring (for example Moles framework).

First what you must do is replacing your service code with:

public class StudentService : IStudentService
{
    private readonly IStudentRepository _studentRepository;

    public StudentService(IStudentRepository studentRepository)
    {
        _studentRepository = studentRepository;
    }

    public IEnumerable<Student> GetAll()
    {
         return _studentRepository.GetAll().ToList();
    }
}

Btw. this is absolutely useless code and example of silly layering which doesn't offer any useful functionality. Just wrapping the call to repository only shows that service is not needed at all as well as unit testing this method is not needed. The main point here is integration test for GetAll method.

Anyway if you want to unit thest such method with MOQ you will do:

[TestClass]
public class StudentsServiveTest
{
    private Mock<IRespository<Student>> _repo;

    [TestInitialize]
    public void Init()
    {
        _repo = new Mock<IRepository<Student>>();
        _repo.Setup(r => r.GetAll()).Returns(() => new Student[] 
            { 
                new Student { StudentId = 1, Name = "A", Surname = "B" },
                new Student { StudentId = 2, Name = "B", Surname = "C" }
            });
    }

    [TestMethod]
    public void ShouldReturnAllStudents()
    {
        var service = new StudentsService(_repo.Object);
        var data = service.GetAll();
        _repo.Verify(r => r.GetAll(), Times.Once());

        Assert.IsNotNull(data);
        Assert.AreEqual(2, data.Count);
    }
}


The issue from what I can see is that you are throwing away the mock object and newing up a new instance

_studentRepository = new StudentRepository(ctx);

Perhaps add a method on the interface to add the context object and reuse the same instance that was injected in the constructor.

using (var ctx = new SchoolContext("SchoolDB"))
    {
        _studentRepository.Context = ctx;
        var students = _studentRepository.GetAll().ToList();
        return students;
    } 
}
0

精彩评论

暂无评论...
验证码 换一张
取 消