Как я могу использовать базы данных Sql CE 4 для функциональных тестов

Из-за потенциальных различий между 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 unit test я инициирую новый экземпляр контекста данных, а затем вызываю context.Database.Delete() и context.Database.Create(). Тестирование устройства выполняется медленнее, но теперь я могу unit test эффективно использовать реальную базу данных


Заключительное редактирование: После нескольких сообщений электронной почты с 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!
        }
    }

Ответ 1

В 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 = "[email protected]" });
        context.SaveChanges();
    }
}

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

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

Ответ 2

Я нашел, что подход в "окончательном редактировании" работает и для меня. Однако это ДЕЙСТВИТЕЛЬНО раздражает. Это не только для тестирования, но и в любое время, когда вы хотите использовать 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.