Думаю, все знают о пользе автотестов.
Они помогают поддерживать код в рабочем состоянии даже при значительных изменениях.
Это также может избавить тестировщиков от утомительной ручной работы и позволить им сосредоточиться на более интересных типах тестирования.
Несмотря на то, что некоторым частям нашего проекта уже более 25 лет, мы находимся только в самом начале внедрения автоматизированного тестирования.
Однако некоторые успехи у нас уже есть, о которых я хочу рассказать в этой статье.
Как писать хорошие автотесты — тема отдельной статьи.
И, вероятно, не один.
Расскажу, как мы реализовали тестирование отдельных компонентов.
Компоненты написаны на C++ и имеют интерфейсы, очень похожие на COM. Мы выбрали Python в качестве языка тестирования и используем очень мощную среду тестирования PyTest. В статье я расскажу о сложностях сочетания C++/COM и Python, подводных камнях, на которые мы наткнулись, и о том, как мы решили эти проблемы.
Отказ от ответственности
- Я не могу просто скопировать и вставить код нашего проекта из-за NDA. Поэтому все примеры в статье были написаны с нуля и никогда не компилировались.
Поэтому могут быть мелкие неточности, синтаксические ошибки или несоблюдение правил форматирования кода.
Но я постарался передать основной смысл.
- Я не эксперт по питону.
Честно говоря, я начал изучать Python примерно в середине проекта.
Поэтому некоторые утверждения относительно Python могут быть либо не совсем верными, либо не до конца разработанными.
- Код Python в примерах может не соответствовать pep8, т.к.
часто отражает прототип SOM и заимствует его стиль.
- Ээта статья не является руководством по Cython, многие вещи остались за кадром
Фон
В проекте, над которым я работаю, мы разрабатываем большой и сложный модуль.Несколько миллионов строк кода на C++, дюжина крупных компонентов и сотня библиотек под капотом.
Этот модуль используется в нескольких огромных приложениях.
Исторически сложилось так, что это всё тестируется только через UI и в основном вручную.
Часто бывает, что изменение в одной области оборачивается ошибкой где-то совсем в другом.
И находят этот баг только через несколько дней, а то и недель.
А иногда что-то всплывает через несколько месяцев, когда другой продукт решает интегрировать новую версию нашего модуля.
У нас есть модульные тесты, которые запускаются на CI при фиксации.
Но когда мы начали говорить о TDD, коду было более 15 лет. Код монолитный и просто запустить его отдельно не получится.
Нам нужен большой рефакторинг, на который никто не даст ресурсов.
Поэтому у нас есть модульные тесты только для простых функций или отдельных классов.
Но так как у модуля есть некий API, то и тестировать этот модуль можно через это API. Мы можем собирать сценарии использования всех приложений, которые нас используют, и писать для этого автоматические тесты.
Тогда можно было бы эти тесты запускать прямо на CI по коммитам.
Так родилась идея компонентного тестирования.
Но кто будет писать тесты? Тестировщики могут придумать хорошие тестовые сценарии и подготовить тестовые данные, но тестировщики не знают C++ (а те, кто знает, быстро становятся разработчиками).
Программисты могли бы писать такие тесты, но обычно их воображения хватает лишь на пару положительных сценариев.
Обычно терпения не хватает охватить все негативные случаи.
Мы решили перенять опыт коллег из соседней команды.
Они сделали обертки для своего компонента с помощью cython и поместили упрощенный интерфейс на Python, который используют для тестов.
Порог входа в Python намного ниже, чем в C++.
Тестировщики могут легко освоить Python за пару дней и начать писать хорошие автотесты.
При чем здесь COM?
Прежде чем мы начнем описывать наши мучения, нужно сказать пару слов о наших интерфейсах.Мы используем технологию, объединенную с COM и портированную на Linux, Mac и Free. Есть некоторые инфраструктурные различия, связанные с отсутствием реестра, но для статьи это не важно.
COM-подобная технология дает нам кучу плюшек, например, готовую инфраструктуру подключаемых компонентов.
Мы можем легко подключать модули, написанные разными командами в разных странах (включая сторонние плагины).
При этом нас не беспокоят вопросы совместимости между различными компиляторами, средами выполнения и стандартными библиотеками.
Также стиль интерфейсов всех модулей, соглашения о передаче параметров и возвращаемых значениях, время жизни объектов — всё это регулируется соглашениями как с COM. Есть и обратная сторона.
Внутри модулей мы можем использовать любые возможности современных стандартов C++.
Но в публичных интерфейсах мы должны придерживаться правил COM — только простые типы или интерфейсы, наследуемые от IUnknown. Нет СТЛ.
Никаких исключений, только HRESULT. Из-за этого код на границах модулей получается очень громоздким и не очень читабельным.
Первый опыт с Цитоном
Для начала мы определили десяток интерфейсов для нашего модуля, с помощью которых можно реализовать небольшой, но полноценный рабочий процесс.Но хотя эти интерфейсы и являются частью общедоступного API, на самом деле они довольно низкоуровневые.
Чтобы выполнить определенную операцию, вы не можете просто вызвать одну функцию или метод. Вам необходимо создать пять объектов, соединить их между собой, запустить на исполнение и ждать результата через будущее.
Вся эта сложность нужна для организации транзакционности, асинхронности, Undo/Redo, организации доступа к потокобезопасным внутренностям и куче других вещей.
Короче говоря, 2 скрина кода на C++ в стиле COM. Мы решили последовать рекомендации наших коллег и написать небольшой слой, который бы скрывал низкоуровневые интерфейсы.
В Python было предложено добавить несколько функций высокого уровня.
Обертка Cython просто перенаправляет вызовы на C++:
Теги: #python #Cython #com #автоматизация тестирования #тестирование ИТ-систем #python #C++cdef class MyModuleObject():
-
Мы Живы - Что Пишут
19 Oct, 24 -
Оповещения От Nagios По Телефону
19 Oct, 24 -
Колония. Глава 1: Пробужденная Жизнь
19 Oct, 24 -
Surtout — Ruby On Rails Cms
19 Oct, 24 -
У 003.Ru Сменился Владелец...
19 Oct, 24