Как я могу использовать базы данных 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.