似乎没有moq EF CodeFirst 4.1。帮助任何人?

时间:2011-04-24 08:04:34

标签: unit-testing mocking moq repository-pattern entity-framework-4.1

我被赋予了评估codeFirst的任务,并且可以用于我们未来的所有项目。 评估基于将codeFirst与现有数据库一起使用。

想知道是否可以使用codeFirst 4.1来模拟存储库。(无假货)

我们的想法是将存储库注入服务并moq存储库。

我一直在网上看,但我只找到了一个使用fakes的例子。我不想使用假货我想使用moq。

我认为我的问题在于DAL的体系结构。(我想使用unitOfWork等...我需要显示一个工作的moq示例)

由于缺乏对Code 4.1的了解,以下是我的尝试(失败)。 我还上传了一个解决方案,以防有人心情愉快并希望改变它。

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

我愿意接受我的Dal.Ideally使用Unity等的建议和完全修改。但我稍后会担心。 最重要的是,我需要能够嘲笑它。如果没有使用MOQ的能力,我们将使用EF 4.1

对项目进行分类

尝试失败

//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 InvalidOperationException("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();
    }
}

再次随意修改或任何需要帮助我一起得到的东西。

非常感谢你的帮助

2 个答案:

答案 0 :(得分:3)

当我开始使用存储库和工作单元模式时,我使用了与this类似的实现(它适用于ObjectContext API,但将其转换为DbContext API很简单)。我们使用MOQ和Unity的实现没有任何问题。当存储库和工作单元的实现发展以及注入的方法时。后来我们发现整个这种方法存在严重的缺陷,但在我引用here的其他问题中已经讨论过了(我强烈建议你仔细阅读这些链接)。

非常令人惊讶的是,您正在评估EFv4.1,特别强调模拟和单元测试,同时您定义的服务方法根本不是单元可测试的(通过模拟)。您的服务方法的主要问题是您没有将存储库/上下文作为依赖项传递,因此您无法模拟它。测试服务并且不使用真实存储库的唯一方法是使用一些very advanced approach =替换模拟和MOQ与绕行(例如Moles框架)。

首先,您必须使用以下内容替换服务代码:

public class StudentService : IStudentService
{
    private readonly IStudentRepository _studentRepository;

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

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

顺便说一下。这是绝对无用的代码和愚蠢分层的例子,它不提供任何有用的功能。只是将调用包装到存储库只显示根本不需要服务以及单元测试不需要此方法。这里的要点是GetAll方法的集成测试。

无论如何,如果你想用MOQ将这种方法单元化,你将会这样做:

[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);
    }
}

答案 1 :(得分:0)

我可以看到的问题是你正在丢弃模拟对象并新建一个新实例

_studentRepository = new StudentRepository(ctx);

也许在接口上添加一个方法来添加上下文对象并重用在构造函数中注入的相同实例。

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