Первый код EF4, TDD, CRUD и транзакции - PullRequest
2 голосов
/ 08 декабря 2010

В прошлом я писал модульные тесты для простых операций CRUD при создании кода доступа к данным / хранилища, который выглядит примерно так:

using(var connection = new WhateverConnection(connectionString))
{
    connection.Open();
    using(var transaction = connection.BeginTransaction())
    {
        try
        {
            //test the CRUD operation
        }
        finally
        {
            //gets rid of any stuff created during the test
            transaction.Rollback();
        }
    }
}

Сегодня я возился с кодом EF4 First и понял, что понятия не имею, как этот сценарий тестирования переводится в лексикон Entity Framework. Похоже, что если я позвоню DbContext.SaveChanges(), он сохранит и коммиты, независимо от того, был ли вызван AcceptAllChanges(). Даже используя ObjectContext вместо DbContext, я не могу понять, как можно воссоздать этот простой тестовый сценарий без очистки вручную созданных макетов / тестовых объектов. Я прочитал эту статью на MSDN , но TransactionScope на самом деле также не имеет метода типа Rollback. Я использую TransactionScope и никогда не звоню Complete? Есть ли другой способ или способ использования DbContext и / или ObjectContext для отката во время модульных тестов? Нужно ли полностью перестраивать свое мышление для TDD с помощью EF4 Code First?

1 Ответ

8 голосов
/ 08 декабря 2010

ObjectContext само не раскрывает транзакционное поведение. Вы должны обернуть код EF в транзакции самостоятельно. Самый простой способ сделать это - использовать TransactionScope. Если вы не вызовете метод Complete в области и удалите его, он выполнит откат. Для этого типа интеграционных тестов мы обычно используем базовый класс:

[TestClass]
public abstract class TransactionTestBase
{
    private TransactionScope scope = null;

    [TestInitialize]
    public virtual void TestInitialize()
    {
      scope = new TransactionScope(TransactionScopeOption.RequiresNew,
          new TransactionOptions()
              {
                  IsolationLevel = IsolationLevel.ReadUncommitted
              });
    }

    [TestCleanup]
    public virtual void TestCleanup()
    {
        if (scope != null)
        {
            scope.Dispose();
            scope = null;
        }
    }
}

Все тестовые классы являются производными от этого класса. TestInitialize вызывается перед каждым TestMethod в производном классе, а TestCleanup вызывается после каждого TestMethod, поэтому вашим тестам вообще не нужно иметь дело с транзакцией.

...