Подтвердить что ты не робот

Как подделать метод DbContext.Entry в Entity Framework с шаблоном репозитория

Поскольку я хочу unit test мой код, я применил шаблон репозитория в своем приложении MVC4. Мне удалось создать контекстный интерфейс, поддельный контекст и использовать поддельную реализацию System.Data.Entity.DbSet, выполнив этот код.

К сожалению, как и два плаката передо мной (здесь и здесь), мне не удается mock DbContext.Entry method. Я использую этот метод для обновления записей базы данных в своем коде следующим образом:

DbContext.Entry(order).State = EntityState.Modified;

Я не нашел решения этой проблемы, только люди, которые говорят такие вещи, как:

"и какова точка модульного тестирования этого кода? Вы подделываете Find метод, то вы подделываете DbEntityEntry и не будет реальной логики для тест".

или

прочитайте этот и все связанные вопросы перед продолжением. (...) Если вы хотите протестировать свои репозитории, создайте тесты интеграции с реальной базой данных.

Это все хорошо и хорошо, но до сих пор нет ответа на вопрос. Я прочитал критику, и мне все еще нужен этот метод Entry, поэтому я смогу использовать поддельный контекст и использовать макетные объекты в моем unit test. Конечно, я буду использовать интеграционные тесты, но они не так быстро, как некоторые быстрые модульные тесты.

Ошибка, которую я получаю, когда я пытаюсь выполнить некоторые реализации, заключается в том, что Error 2 'Project.Models.Order' does not contain a definition for 'State' and no extension method 'State' accepting a first argument of type '[whatever return type I use]' could be found (are you missing a using directive or an assembly reference?)

Я надеюсь, что кто-то может помочь мне создать поддельный метод DbContext.Entry.

4b9b3361

Ответ 1

Нашел ответ здесь, добавив дополнительный уровень косвенности. Мы получаем:

public void SetModified(object entity)
{
    Entry(entity).State = EntityState.Modified;
}

и используйте DbContext.SetModified(entity) в нашем контроллере.

Ответ 2

Пример того, как реализовать репозитории на основе интерфейса и единицу работы, чтобы получить то, что вам нужно:

public interface IRepository<T>
    {
        T FindSingle(Expression<Func<T, Boolean>> predicate, params Expression<Func<T, object>>[] includeExpressions);
        void ProxyGenerationOn();
        void ProxyGenerationOff();
        void Detach(T entity);
        void Add(T newEntity);
        void Modify(T entity);
        void Attach(T entity);
        void Remove(T entity);
        void SetCurrentValues(T modifiedEntity, T origEntity);
        T GetById(int id);
        T GetById(int id, bool sealOverride);
        IQueryable<T> GetAll();
        IQueryable<T> GetAll(bool sealOverride);
        IQueryable<T> GetAll(string[] EagerLoadPaths);
        IQueryable<T> Find(Expression<Func<T, Boolean>> predicate);
    }



public interface IUnitOfWork : IDisposable
    {
       //repository implementations go here
       bool SaveChanges()
     }

Обратите внимание, что контекст полностью абстрагирован. Вам нужно только беспокоиться об этом в конкретных реализациях.

Ответ 3

Чтобы обойти это, я добавил метод overload и добавил устаревший атрибут, чтобы увидеть, где был вызван оригинальный метод.

    public virtual void Entry<TEntity>(TEntity entity, Action<DbEntityEntry<TEntity>> action) where TEntity : class
    {
        action(base.Entry(entity));
    }

    [Obsolete("Use overload for unit tests.")]
    public new DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class
    {
        return base.Entry(entity);

        /** or **/

        throw new ApplicationException("Use overload for unit tests.");
    }

то вы можете DbContext.Entry(order, ent => ent.State = EntityState.Modified;