Moles
Какое-то время назад, при выборе Isolation фреймвёрка для написания тестов мой выбор пал на Moles. Выбрал его я, руководствуясь следующими фактами: большой общественный резонанс (на хабре статьи про него, как пирожки вылетают), громкое имя разработчика - Microsoft Research как ни как. Ну и, посмотрев пару замечательных примеров, начал использовать его сначала в своих домашних разработках, а потом и на основной работе в достаточно большом проекте.
Первое впечатление от фреймверка было очень положительным, молится (мочится, глушится, стабится, фейчется и т.п. действия приводящие к подмене готового кода, или использование не существующих реализаций интерфейсов во время тестов далее будут обозначены глаголом молить, с ударением на первый слог) все делается очень быстро, просто и достаточно красиво, с эстетической точки зрения.
Самый первый звоночек, говорящий, что здесь все не так радужно как кажется прозвенел при первых запусках тестов - достаточно долгое время запуска тестов. Таким образом, на подготовку тестов к запуску уходило секунд 20-30 и это не на самой плохой машине. Это время идёт на инициализацию платформы Moles, многие скажут - полминуты это не много, тем более только один раз при запуске набора тестов, но раздражает. Справедливости ради замечу, что такая долгая инициализация происходит, если использоваться host type для исполнения тестов - moles. Для тех, кто не знает, Moles могут работать в двух ипостасях: moles и stubs. Первые используют для исполнения тестов собственный хост, вторые исполняются в обычной среде.
Поначалу меня вполне устраивали эти небольшие полминутные задержки, дающие действительно уникальные возможности,
например, подмена статического метода (поля) DateTime.Now. Собственно никакого другого применения для молей, кроме
как подмены готовых статических или sealed членов я и не нашел. Дальше - больше, при использовании этого волшебного
хоста, как оказалось ни решарпер ни TeamCity не могут правильно считать покрытие тестами.
Наверно, это и стало главной проблемой, приведшей к отказу в проекте от moles хоста, а, следовательно, и молей как таковых.
Благо проект только начинал писаться, и тестов работающих под этим хостов было не много, а развивающийся проект
позволял изменить архитектуру таким образом, чтобы без таких тестов можно было обойтись.
Собственно после отказа от moles хоста пользование этого фреймвёрка стало приносить гораздо меньше хлопот.
Практически не вызывает проблем и сейчас - фреймвёрк удобный.
Так же сразу скажу еще про несколько, не удобных моментов:
- Для использования заглушек используются генерируемые самим фреймвёрком сборки, создающиеся на основе указанных разработчиком. Сам процесс создания таких сборок заглушек практические не причиняет неудобств - тыкнул раз на сборке и пользуйся. А вот тот факт, что для заглушки только что объявленного члена интерфейса требуется скомпилить сборку и только потом глушить в тестах - раздражает.
- Удаление однажды созданной сборки требует выполнения аж трёх действий: удаление самого референса, удаление конфигурационного файла (*.moles) и удаление кеша этих сборок из папки проекта.
- Если использовать стаб по умолчанию, то обращение к любому не заглушеному члену интерфейса
приводит к генерации исключения в момент выполнения, поэтому, в большинстве случаев создание стаба выглядит так:
var createCommand = new SIDbCommandЭто как минимум занимает много места, да и вообще много лишних букв.
{
InstanceBehavior = BehavedBehaviors.DefaultValue
....
};
Moq
Не смотря на все плюшки и печеньки Moles, досадные раздражители заставили как минимум посмотреть на другие фреймвёрки. Следующим, кто попался на глаза, был Moq. Сразу скажу, что с Moq я только начал работать, поэтому много не знаю и не умею, так что буду рад конструктивной критике. Первое впечатление, которое, безусловно, было от долгой работы с Moles - всё по другому. Сначала фраймвёрки кажутся абсолютно разными и даже не верится, что призваны исполнять одни и те же функции. Собственно самые главные недостатки Moles в Moq исключены архитектурно:
- Для использование moq не нужно генерировать никаких сборок - всё заменяет волшебных класс Mock.
- Можно молить еще не скомпиленные интерфейсы, что позволяет писать тесты до начала любой реализации.
- Явно не заглушенные члены интерфейса наконец не нужно никак выделять - они по умолчанию работают как надо.
Таким образом, аналогичный выше приведенный код на Moq будет выглядить примерно так:
var createCommand = new Mock<IDbCommand>().Object;
- Не возможно молить статически и sealed члены.
- Очень слабый лямбда синтаксис. Да и Fluent тоже какой-то не такой. Короче синтаксис специфичный.
Не скажу, что ужасный, но в Moles значительно читабельнее и понятнее, хотя опять же возможно дело привычки.
В общем, сравнивайте сами
createCommand.ExecuteScalar = () => 100500; // Moles
createCommand.Setup(x => x.ExecuteScalar()).Returns(100500); // Moq
В некоторых моментах использование Moq является намного удобнее Moles. Например, чтобы убедиться, что был вызван метод в Moles приходилось делать так:
var databaseStrategy = new SIDatabaseStrategy
{
InstanceBehavior = BehavedBehaviors.DefaultValue,
IsTableExistString = table => !table.Equals("dbullet"),
CreateTableTable = table => { tableWasCreated = Equals(table.Name, "dbullet"); }
};
target.InitDatabase(GetType().Assembly);
Assert.IsTrue(tableWasCreated);
target.InitDatabase(GetType().Assembly);
databaseStrategy.Verify(x => x.CreateTable(It.Is<Table>(y => y.Name == "dbullet")), Times.Once());
Опять не получается написать все за один подход, поэтому продолжить постараюсь в следующий раз. Но предварительный вывод такой - если Вы начинаете писать новый проект или есть возможность изменять архитектуру старого, то стоит посмотреть на что-то более легковеснее чем Moles (читай Moq), если же требуется молить что-то не доступное для изменения (сторонние сборки, древний унаследованный код, плохо спроектированный код) - Moles Вас очень сильно выручит. Я же свои булеты перевел на Moq, хотя пока и не в основной ветке. Уже сейчас можно там посмотреть как выглядят аналогичные тесты на Moq и Moles, ну или можно дождаться следующей моей заметки.