Кажется, не может moq EF CodeFirst 4.1. Помогите кому-нибудь? - PullRequest
2 голосов
/ 24 апреля 2011

Мне было дано задание оценить codeFirst и возможность его использования для всех наших будущих проектов. Оценка основана на использовании codeFirst с существующей базой данных.

Интересно, можно ли смоделировать хранилище, используя codeFirst 4.1. (Без подделок)

Идея состоит в том, чтобы внедрить репозиторий в сервис и moq репозитория.

Я искал в сети, но нашел только пример использования подделок. Я не хочу использовать подделки, я хочу использовать moq.

Я думаю, что моя проблема в архитектуре DAL. (Я хотел бы использовать unitOfWork и т. Д., Мне нужно показать работающий пример moq)

Ниже приведена моя попытка (с треском провалившаяся) из-за недостатка знаний о коде вначале 4.1. Я также загрузил решение на случай, если у кого-то хорошее настроение и он хочет его поменять.

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

Я открыт для предложений и полной модификации моего Dal. Идеально использую 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 ]

3 голосов
/ 24 апреля 2011

Когда я начинал с хранилища и шаблонов единиц работы, я использовал реализацию, аналогичную this (это для API ObjectContext, но преобразовать его в API DbContext просто).Мы использовали эту реализацию с MOQ и Unity без каких-либо проблем.К тому времени реализация репозитория и единицы работы развивалась так же, как подход инъекций.Позже мы обнаружили, что у всего этого подхода есть серьезные подводные камни, но это уже обсуждалось в других вопросах, на которые я ссылался здесь (я настоятельно рекомендую вам пройти по этим ссылкам).

Это очень удивительночто вы оцениваете EFv4.1 с большим акцентом на макетирование и модульное тестирование, и в то же время вы определили метод обслуживания, который вообще не тестируется модулем (с имитацией).Основная проблема вашего сервисного метода заключается в том, что вы не передаете репозиторий / контекст как зависимость, и из-за этого вы не можете имитировать его.Единственный способ протестировать ваш сервис и не использовать настоящий репозиторий - это использовать очень продвинутый подход = заменить mocking и MOQ на обход (например, Moles Framework).

Первое, что вынеобходимо заменить ваш сервисный код на:

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);
    }
}
0 голосов
/ 24 апреля 2011

Проблема, которую я вижу, заключается в том, что вы выбрасываете фиктивный объект и обновляете новый экземпляр

_studentRepository = new StudentRepository(ctx);

Возможно, добавьте метод в интерфейс, чтобы добавить объект контекста и повторно использовать тот же экземпляр, который был внедрен в конструктор.

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