Как я могу обнаружить изменения IDbSet

Я создаю фиктивный IDbSet, чтобы позволить, среди прочего, модульное тестирование классов структуры сущностей.

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

public interface IReportContext
{
    IDbSet<Report> Reports {get;}
    public int SaveChanges();
}

public class MockReportContext : IReportContext
{
    IDbSet<Report> Reports {get;}

    public int SaveChanges()
    {
        //Need to detect changes here???
    }

    public MockReportContext()
    {
       Reports = new MockDbSet<Report>();
    }
}

public class MockDbSet<T> : IDbSet<T>
{
    readonly ObservableCollection<T> _data;
    readonly IQueryable _query;

    public FakeDbSet()
    {
        _data = new ObservableCollection<T>();
        _query = _data.AsQueryable();
    }

    public FakeDbSet(ObservableCollection<T> data)
    {
        _data = data;
        _query = _data.AsQueryable();
    }

    public virtual T Find(params object[] keyValues)
    {
        throw new NotImplementedException();
    }

    public T Add(T item)
    {
        _data.Add(item);
        return item;
    }

    public T Remove(T item)
    {
        _data.Remove(item);
        return item;
    }

    public T Attach(T item)
    {
        _data.Add(item);
        return item;
    }

    public T Detach(T item)
    {
        _data.Remove(item);
        return item;
    }

    public T Create()
    {
        return Activator.CreateInstance<T>();
    }

    public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
    {
        return Activator.CreateInstance<TDerivedEntity>();
    }

    public ObservableCollection<T> Local
    {
        get { return _data; }
    }

    Type IQueryable.ElementType
    {
        get { return _query.ElementType; }
    }

    System.Linq.Expressions.Expression IQueryable.Expression
    {
        get { return _query.Expression; }
    }

    IQueryProvider IQueryable.Provider
    {
        get { return _query.Provider; }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        return _data.GetEnumerator();
    }
}

Это прекрасно работает для добавления, удаления и извлечения объектов. Однако, когда я пытаюсь следующее:

IReportContext context = new MockReportContext();
context.Reports.Add(new Report()); //Works
Report report = context.Reports.First(); //Works
report.Message = "Hello World!";
context.SaveChanges(); //Does nothing

Как получается, что MockReportContext мог знать, что объект отчета, который он возвратил, изменился?? Я знаю, что с помощью Entity Framework это возможно, но я понятия не имею, как...

1 ответ

Решение

Я думаю, что вы в основном там, но я бы предложил использовать насмешливый фреймворк, такой как Moq (мои личные предпочтения) или Rhino Mocks, чтобы высмеивать IReportContext для ваших модульных тестов, вместо того, чтобы создавать проблемы типа Fake вроде MockReportContext, (Хорошо, изучение фальшивых фреймворков сопряжено с некоторыми трудностями, но в будущем это избавит от рутинной работы с поддельными классами.)

Я бы предположил, что вы юнит тестирование кода, который зависит от IReportContextтак что вам не нужно ничего делать внутри SaveChanges(), вам просто нужно подтвердить, что ваш код вызывал SaveChanges() внутренне, если это предполагалось.

Вот хороший обзор использования Moq с производными классами DbContext / DbSet Entity Framework.

В приведенном выше связанном обзоре, если в конце модульное тестирование тестировало метод, который вызывает внутренне SaveChanges(), это может дополнительно проверить, что SaveChanges() действительно был вызван вашим методом со строкой:

dc.Verify(db => db.SaveChanges());

Вы также можете сделать это с вашим MockReportContext класс, установив для свойства side значение True внутри SaveChanges() Класс и проверка его в конце вашего модульного теста, но фреймворк гораздо более гибок и избавит от необходимости писать дополнительные классы для модульных тестов.

Если вы хотите избежать использования фальшивых фреймворков, то вот как это сделать с помощью подделок.

Другие вопросы по тегам