Как я могу использовать базы данных Sql CE 4 для функциональных тестов - PullRequest
10 голосов
/ 13 января 2011

Из-за потенциальных различий между Linq-to-Entities (EF4) и Linq-to-Objects мне нужно использовать реальную базу данных, чтобы мои классы запросов корректно извлекали данные из EF. Sql CE 4, кажется, идеальный инструмент для этого, однако я столкнулся с несколькими сбоями. Эти тесты используют MsTest.

Проблема, с которой я столкнулся, заключается в том, что если база данных не воссоздается (из-за изменений модели), данные продолжают добавляться в базу данных после каждого теста, и ничто не избавляет от данных. Это может привести к конфликтам в тестах, так как запросы возвращают больше данных, чем предполагалось.

Моей первой идеей была инициализация TransactionScope в методе TestInitialize и размещение транзакции в TestCleanup. К сожалению, Sql CE4 не поддерживает транзакции.

Моя следующая идея состояла в том, чтобы удалить базу данных в TestCleanup с помощью вызова File.Delete(). К сожалению, кажется, что это не работает после запуска первого теста, так как первый тест TestCleanup, кажется, удаляет базу данных, но каждый тест после первого, похоже, не создает базу данных заново, и, таким образом, выдает ошибку, которая файл базы данных не найден.

Я попытался изменить теги TestInitialize и TestCleanup на ClassInitialize и ClassCleanup для моего класса тестирования, но с ошибкой NullReferenceException из-за теста, выполненного до ClassInitialize (или около того ClassInitialize находится в базовом классе, так что, возможно, это его вызывает).

У меня закончились способы эффективного использования Sql CE4 для тестирования. У кого-нибудь есть идеи получше?


Редактировать: В итоге я придумал решение. В моем базовом классе модульных тестов EF я инициирую новый экземпляр моего контекста данных, а затем вызываю context.Database.Delete() и context.Database.Create(). Модульные тесты работают немного медленнее, но теперь я могу эффективно тестировать их, используя реальную базу данных


Окончательное редактирование: После некоторых писем назад и вперед в Microsoft выясняется, что TransactionScope теперь разрешено в SqlCE с последней версией SqlCE. Однако, если вы используете EF4, существуют некоторые ограничения в том, что вы должны явно открыть соединение с базой данных до начала транзакции. В следующем коде показан пример успешного использования Sql CE для модульного / функционального тестирования:
    [TestMethod]
    public void My_SqlCeScenario ()
    {
        using (var context = new MySQLCeModelContext()) //ß derived from DbContext
        {
            ObjectContext objctx = ((IObjectContextAdapter)context).ObjectContext;
            objctx.Connection.Open(); //ß Open your connection explicitly
            using (TransactionScope tx = new TransactionScope())
            {

                var product = new Product() { Name = "Vegemite" };
                context.Products.Add(product);
                context.SaveChanges();
            }
            objctx.Connection.Close(); //ß close it when done!
        }
    }

Ответы [ 2 ]

4 голосов
/ 13 января 2011

В вашем TestInitialize вы должны сделать следующее:

System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
    new System.Data.Entity.Database.DropCreateDatabaseAlways<YourEntityFrameworkClass>());

Это приведет к тому, что структура сущностей всегда будет воссоздавать базу данных при каждом запуске теста.альтернативный класс, который наследуется от DropCreateDatabaseAlways.Это позволит вам каждый раз заполнять базу данных установленными данными.

public class DataContextInitializer : DropCreateDatabaseAlways<YourEntityFrameworkClass> {
    protected override void Seed(DataContext context) {
        context.Users.Add(new User() { Name = "Test User 1", Email = "test@test.com" });
        context.SaveChanges();
    }
}

Затем в вашей инициализации вы измените вызов на:

System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
    new DataContextInitializer());
3 голосов
/ 17 марта 2011

Я обнаружил, что подход в «окончательном редактировании» работает и для меня.Тем не менее, это действительно раздражает.Это не только для тестирования, но и в любое время, когда вы захотите использовать TransactionScope с Entity Framework и SQL CE.Я хочу написать код один раз, и мое приложение поддерживает и SQL Server, и SQL CE, но везде, где я использую транзакции, я должен это делать.Конечно, команда Entity Framework должна была бы справиться с этим за нас!

Тем временем я сделал еще один шаг вперед, чтобы сделать его немного чище в моем коде.Добавьте этот блок к своему контексту данных (независимо от того, какой класс вы производите от DbContext):

public MyDataContext()
{
    this.Connection.Open();
}

protected override void Dispose(bool disposing)
{
    if (this.Connection.State == ConnectionState.Open)
        this.Connection.Close();

    base.Dispose(disposing);
}

private DbConnection Connection
{
    get
    {
        var objectContextAdapter = (IObjectContextAdapter) this;
        return objectContextAdapter.ObjectContext.Connection;
    }
}

Это делает его намного чище, когда вы на самом деле его используете:

using (var db = new MyDataContext())
{
    using (var ts = new TransactionScope())
    {
        // whatever you need to do

        db.SaveChanges();
        ts.Complete();
    }
}

Хотя я предполагаюесли вы разрабатываете свое приложение так, что все изменения фиксируются в одном вызове SaveChanges (), то неявная транзакция будет достаточно хорошей.Для сценария тестирования мы хотим откатить все назад вместо вызова ts.Complete (), так что это, безусловно, требуется там.Я уверен, что есть другие сценарии, в которых нам нужна доступная область транзакции.Жаль, что он не поддерживается напрямую EF / SQLCE.

...