Pull to refresh

Тестируем EntityFramework запросы с помощью SQL Server Compact Edition

Reading time 3 min
Views 4K
Как и все серьезные люди мы задумались о широком использвании unit тестов. В качестве платформы взяли MSpec из-за того, что некоторым членам комманды очень нравится то, что названия тестов выглядят так, словно читаешь текст, но при этом в отличии от огурца и ему подобных не приходится писать громоздкие парсеры.

Unit тестирование и моки — это безусловно хорошо и нужно, но иногда хочется и проверить, что кусок кода, работающий с базой отрабатывает корректно вцелом. Да и отчку останова в нужном месте хочется поставить и зайти туда не запуская все тяжелое приложение. В общем возникло желание тестировать базу.

Сначала решили попробовать SQLite. Маленький, теоритически много чего умеющий, в общем все с ним знакомы. Который и был запинан в качестве первого варианта, но после бодания с тем что запросы EntityFramework провайдером для SQLite строятся совсем не так как для SQL server — решено было искать ближе к корням — к Microsoft. Благо, как всегда в мире .NET, реализаций чего-либо на выбор не много, но они достаточо сурьезные. В общем решено было завести SQL Server CE.

Сразу возникает сложность. Edmx файл мы хотим под MSSQL и чтобы в нем самом никаких отсылок к SQL CE не было. Решаем этот вопрос просто и доблестно. Добавляем в проект с тестами t4 template (.tt) который производит и некоторые необходимые манипуляции с описанием схемы.

Для начала привычной рукой ставим Nuget пакеты
Install-Package SqlServerCompact
Install-Package EntityFramework.SqlServerCompact


Далее достаем из edmx SSDL и заменяем в ней Provider на SQL CE, и приводим типы из специфичных для SQL Server в SQL CE подмножество.
...
<# 
var di = new DirectoryInfo(Host.ResolvePath("..\\DataAccessLayer\\")).FullName;
var xmlDocument = new XmlDocument();
xmlDocument.Load(Path.Combine(di,"Entities.edmx"));
var xmlNamespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
xmlNamespaceManager.AddNamespace("edmx", "http://schemas.microsoft.com/ado/2009/11/edmx");
xmlNamespaceManager.AddNamespace("ssdl", "http://schemas.microsoft.com/ado/2009/11/edm/ssdl");

// выкусываем SSDL из EDMX

var selectSingleNode = xmlDocument.SelectSingleNode("//ssdl:Schema[@Namespace='ТУТ_ИМЯ_НУЖНОЙ_СХЕМЫ']", xmlNamespaceManager);

// Заменяем Provider 

((XmlElement) selectSingleNode).SetAttribute("Provider", "System.Data.SqlServerCe.4.0");

// обрабатываем типы
// например
foreach (XmlElement node in xmlDocument.SelectNodes("//ssdl:Property[@Type='varbinary(max)']", xmlNamespaceManager))
{
	node.SetAttribute("Type","image");
}
        
#>


После этого получаем специально подготовленную ssdl, которую мы хотим скормить нашему контексту.
Сперва мы делаем ее ресурсом — в свойствах объекта и на всякий случай в проекте можно прописать более вменяемое чем стандартное имя тэгом LogicalName
    <EmbeddedResource Include="SqlCompact.ssdl">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>SqlCompact.tt</DependentUpon>
      <LogicalName>SqlCompact.ssdl</LogicalName>
    </EmbeddedResource>

В шаблонах, создаваемых EF для контекста мы конструктор по умолчанию сделали приватным, а публичный конструктор, который добавили, всегда требует наличие параметра IConnectionString, реализации которого занимаются выдачей ConnectionString для всех желающих. В коде проекта этот интерфейс мапится на выдачу стандартной реализации. Для тестов реализация IConnectionString должна возвращать provider как System.Data.SqlServerCe.4.0 и Persist Security Info=True — иначе не взлетит. Кроме того в connectionString обязательно нужно заменить оригинальную ssdl на сгенерированую и вкомпилированую в ресурс выше.

Начинаем писать тесты, для которых базу создаем entities.Database.Create() и радумеся возможности тестировать чуть больше чем раньше.

Итого:
1) Ставим пакеты
2) Выкусываем SSDL с помощью t4 и меняем в ней типы
3) Меняем provider в SSDL и в connectionstring (если nuget глюкнул — то и в App.config)

Если же хочется все-таки SQLite — то алгритм схожий, но созданием таблиц придется заняться самостоятельно из-за корявой реализации autoincrement типа в оной и можно забыть о внешних ключах.
Tags:
Hubs:
+5
Comments 2
Comments Comments 2

Articles