Юнит-тесты, BDD и сила текучих утверждений (fluent assertions) в 1С

Немного истории Благодаря классному дядьке Кенту Беку (Kent Beck) родилась замечательная методология test-driven development. Не смотря на необычность подхода, переворачивающего привычный процесс написания кода с ног на голову (тест на функционал создается до реализации), сейчас уже можно сказать, что разработка через тестирование стала стандартом де-факто. Практически в любых вакансиях фигурирует требование к знанию и опыту использования методики TDD и соответствующих инструментов. Почему, казалось бы, ломающая привычную парадигму мышления методология прижилась и стандартизировалась? Потому что «Жизнь слишком коротка для ручного тестирования», а писать авто-тесты на существующий код иногда просто не возможно, ведь код, написанный в обычной парадигме, как правило совершенно тесто-не-пригодный.Стоит отметить, что за время своего существования методология успела обзавестись ответвлением (fork) в виде BDD. Дэн Норт (Dan North) в своей статье (Introducing BDD) указал на сложности внедрения TDD среди разработчиков и для решения обозначенных проблем предложил практику, которая называется behaviour-driven development. Основной фишкой BDD можно назвать микс из TDD и DDD, которая в начале выражалась в правильном именовании тестовых методов (названия тестовых методов должны быть предложениями). Апогеем BDD, на текущий момент, можно считать рождение языка Gherkin и инструментария, который его использует (Cucumber, RSpec и т.п.).

К чему я веду и при чем тут 1С? В мире 1С TDD только только начинает набирать популярность. Я еще не видел вакансий разработчиков 1С с требованием знания TDD. Стоит признать, что существенным препятствием является отсутствие в ядре платформы 1С инструментов для написания тестов до кода.Так что же у нас есть на текущий момент для разработки через тестирование в мире 1С? xUnitFor1C — вполне себе зрелый проект, позволюящий разрабатывать в стиле TDD. Vanessa-behavoir — спецификации на языке Gherkin и т.п., пока что не в релизном состоянии. А теперь вопрос, который должен возникать у любого уважающего себя члена общества: «Как лично я могу помочь… (в моем случае — миру 1С разработки перейти на передовые методологии)?».Прежде чем ответить на этот вопрос, я хочу коснуться темы хорошо написанных утверждений в тестах. Утверждения обозначают ожидаемое поведение нашего кода. Одного взгляда на утверждения должно быть достаточно, чтобы понять, какое поведение тест пытается до нас донести. К сожалению, классические утверждения не позволяют этого достичь. Зачастую нам приходится долго вчитываться и расшифровывать замысел автора теста.К счастью, в последнее время появилась тенденция к применению текучих интерфейсов (fluent interface), что очень положительно сказывается на наглядности и интуитивной понятности кода. Инструментарий для тестирования так же не остался в стороне.оявились текучие утверждения, называемые так же утверждениями в стиле BDD. Они позволяют формулировать утверждения в более естественной, удобной и выразительной манере.Впервые я столкнулся с подобным подходом в NUnit в модели утверждений на основе ограничений (Constraint-Based Assert Model).Много позже я познакомился со связкой mocha.js + chai.js, которая у меня вызвала полнейший восторг.

Так вот, мой ответ на вопрос «Как лично я могу помочь миру 1С разработки перейти на передовые методологии?» — текучие утверждения… для начала.

Разработка текучих утверждений для платформы 1С Как заправский разработчик через тестирование, я начал разработку с теста. Первый тестовый метод содержал всего 1 строку: Ожидаем.Что (5).Равно (5); Реализация оказалась на удивление простой. Переменная Ожидаем содержит объект ВнешняяОбработка (далее объект-утверждения), у этого объекта есть экспортные методы: Что (ПроверяемоеЗначение) — сохраняет в контексте объекта-утверждения проверяемое значение; Равно (ОжидаемоеЗначение) — проверяет на равенство ранее сохраненное значение с переданным ожидаемым значением. В случае неравенства выбрасывается исключение с описанием ошибки утверждения. Каждый метод возвращает тот же самый объект-утверждения.Следующим шагом, сигнатура метода Что была расширена необязательным параметром Сообщение, которое делает выбрасываемые утверждениями исключения более информативными.

Далее я задумался над тем, что делать с утверждением НеРавно. Должно ли быть такое утверждение? В классических утверждениях так и есть, почти каждое утверждение имеет своего антипода (Равно/НеРавно, Заполнено/НеЗаполнено и т.д.). Но только не в текучих утверждениях! Так родился тест №2:

Ожидаем.Что (5).Не.Равно (7); Выглядит красиво, но не реализуемо на языке 1С. Еще попытка: Ожидаем.Что (5).Не ().Равно (7); По прежнему красиво и казалось бы реализуемо. Нужно всего лишь взвести флаг отрицания в контексте объекта-утверждения, и затем любое следующее по цепи утверждение проверять с учетом этого флага. По сути нужен был XOR, на языке 1С это выглядит вот так: РезультатУтверждения = ФлагОтрицания <> ЛогическоеВыражениеУтверждения; Но платформа отказалась компилировать объект с методом Не (). Дело в том, что Не — зарезервированное слово, ограничение на его использование распространяется в т.ч. и на имя метода. Мозговой штурм с коллегами не позволили красиво обойти эту проблему, поэтому финальный вариант с отрицанием выглядит так: Ожидаем.Что (5).Не_().Равно (7); Если кто-то предложит лучшее решение обозначенной проблемы, я буду очень признателен. Вариант замены русских букв на латиницу не предлагать! В итоге родился следующий API Что (ПроверяемоеЗначение, Сообщение = ») — сохраняет в контексте внешней обработки проверяемое значение и дополнительное сообщение для исключений выбрасываемых утверждениями.Не_() — отрицает любое утверждение следующее по цепи.

ЭтоИстина () — утверждает, что проверяемое значение является Истиной.

ЭтоЛожь () — утверждает, что проверяемое значение является Ложью.

Равно (ОжидаемоеЗначение) — утверждает, что проверяемое значение равно ожидаемому.

Больше (МеньшееЗначение) — утверждает, что проверяемое значение больше, чем переданное в утверждение.

БольшеИлиРавно (МеньшееИлиРавноеЗначение) / Минимум (МинимальноеЗначение) — утверждает, что проверяемое значение больше или равно переданному в утверждение.

МеньшеИлиРавно (БольшееИлиРавноеЗначение) / Максимум (МаксимальноеЗначение) — утверждает, что проверяемое значение меньше или равно переданному в утверждение.

Меньше (БольшееЗначение) — утверждает, что проверяемое значение меньше, чем переданное в утверждение.

Заполнено () — утверждает, что проверяемое значение отличается от значения по умолчанию того же типа.

Существует () — утверждает, что проверяемое значение не Null и не Неопределено.

ЭтоНеопределено () — утверждает, что проверяемое значение это Неопределено.

ЭтоNull () — утверждает, что проверяемое значение это Null.

ИмеетТип (Тип) — утверждает, что проверяемое значение имеет переданный в утверждение тип или имя типа.

Между (НачальноеЗначение, КонечноеЗначение) — утверждает, что проверяемое значение находится между переданными в утверждение значениями.

Содержит (ИскомоеЗначение) — утверждает, что проверяемое значение содержит переданное в утверждение. Применяется для строк и коллекций.

ИмеетДлину (ОжидаемаяДлина) — утверджает, что проверяемое значение имеет длину переданную в утверждение. Применяется для строк и коллекций.

Примеры использования Ожидаем.Что (1 > 0).ЭтоИстина (); Ожидаем.Что (13 = 2).Не_().ЭтоИстина (); Ожидаем.Что (5 = 7).ЭтоЛожь (); Ожидаем.Что (5).Равно (5); Ожидаем.Что (4).Больше (2); Ожидаем.Что (7).БольшеИлиРавно (7); Ожидаем.Что (НекийМассив.Количество ()).Минимум (9); Ожидаем.Что (90).МеньшеИлиРавно (100); Ожидаем.Что (СтрДлина (НекаяСтрока)).Максимум (90); Ожидаем.Что (55).Меньше (56); Ожидаем.Что (1).Заполнено (); Ожидаем.Что (Новый Массив).Существует (); Ожидаем.Что (Неопределено).ЭтоНеопределено (); Ожидаем.Что (ВыборкаИзБД.НекоеПоле).ЭтоNull (); Ожидаем.Что (»).ИмеетТип («Строка»); Ожидаем.Что (7).Между (1, 10); Ожидаем.Что («Некая строка»).Содержит («стр»); Ожидаем.Что («Некая строка»).ИмеетДлину (12); Пример немного сложнее: Ожидаем.Что («Некая строка») .Существует () .Не_().ИмеетТип («Число») .ИмеетДлину (12) .Не_().Содержит (»!!!»); Послесловие Разработка доступна на github. Как, наверное, заметил читатель с пытливым умом, ссылка ведет на нечто большее, чем просто на библиотеку утверждений. Но это уже материал для следующей статьи.

© Habrahabr.ru