Не совсем согласен, в реальности у нас происходит сильное уменьшение объёмов тестирования, разделение тестов да и просто включение логики - что имеет смысл тестировать.
Например позитивные и негативные тесты уже серьёзно уменьшают эту цифру - если мы используем целочисленное поле, то проверить его на входе более чем 3 позитивными параметрами не имеет смысла. Негативные сценарии это уже вопрос валидации, если же её нет, то это весьма значимый вопрос к проектированию кода - где она? По факту любое непопадание в валидацию сводит на нет попарное тестирование, так как при любой из комбинаций если хоть одно поле не валидируется, то у нас идёт отказ на обработку. И здесь мы просто проходимся по каждому полю индивидуальным тестом уменьшая объём передаваемых параметров только до тех которые реально дают понимание проблемы или ситуации.
Ещё ситуация которая эту историю сильно разгружает - обязательные поля. Хоть одно обязательное поле есть всегда, чаще их от 3 до 5. То есть фактически мы уменьшаемся по количеству комбинаций на десятки процентов. И всё это вместе даёт по моему опыту 300-1500 позитивных комбинаций, плюс штук 20-30 тестов на негативные истории.
Если честно, то я считаю, что ручное тестирование, которое дублирует автоматизацию это скорее поражение, так как стремятся тестировать руками всё подряд вместо тщательного контроля того, что недоступно автоматизации. Например при выборе правильного интерфейса тестирования, мы можем процентов на 99 закрыть автоматизацией тестирование API, но тестировщики руками упорно продолжают слать строки в целочисленные поля. Или ещё хуже тестируют руками бекенд через фронтенд. И как итог вместо того, чтобы добавить к автоматизации кейсы ручного тестирования, задачи дублируются. На самом деле тут причина не в том, что специалисты тестирования тупые, а в том, что системы очень часто не проектируются под возможность тестирования. И яркое этому подтверждение появление такой позиции как SDET. Так как приходится ещё и в разработке шарить, чтобы создать дополнительную нашлёпку, чтобы автотесты вобще возможны были.
Был рад увидеть ваш комментарий. После отправки своего комментария, я решил было, что занимаюсь некромантией.
Не вижу никаких проблем в полном переборе, pairwise будет просто подмножеством и всё. Потом потребуется 3wise, 4wise и тп. Так что смысл уходить от этого по факту теряется. Сложности будут если поля взаимоисключающие, но тут скорее вопрос почему вообще возникла эта ситуация? В крайнем случае можно сделать немного грязно, если взаимоисключающие поля есть, то сделать список исключений для этих пар полей и просто в тесте его пропускать, а сам список отдать в другой тест, чтобы этот список исключений мучить там.
Если честно, для обязательных полей у меня есть решение, их достаточно передавать в параметризации как словарь или список, а в теле теста просто распаковать, и собрать в общую кучу с остальными.
На самом деле тестировщик я довольно своеобразный, я применяю TDD и это сказывается на подходах к тестированию.
Хорошая статья, нет правда, всегда радует, что кто-то думает схоже.
Попробую немного добавить по некоторым моментам.
Паттерн Фабрика - мне кажется он несколько недооценён в Питоне по причине того, что мы пытаемся использовать его слишком развёрнуто. Думаю, что имеет смысл добавить немного простоты, если фабрику оформить как функцию, в которую аргументом ты кидаешь класс описывающий конфигурацию и затем она собирает в единый объект всё приложение возвращая его, то получается весьма понятно и гибко. Конечно вероятность того, что нам понадобится миллион экземпляров приложения с чуть-чуть разными параметрами нет так много. Но всё же есть и эту вероятность закрывают тесты - так мы можем вариативно тестировать приложение. Например, банально отключая какую-то фичу и прогоняя весь набор тестов (несколько искусственный пример). Если кто использует pytest очень рекомендую фикстуры-фабрики.
Ошибки и исключения - Хочу добавить, что имеет смысл построить дерево исключений в самой верхушке которого будет что-то типа MySuperAppException(Exception), а все остальные исключения наследовать от него и давать логичные названия. И взять за правило, что эти два слова будут только в одном месте проекта. Тогда перехватить всё подряд будет значительно сложнее. Ещё repr(e)в некоторых ситуациях может быть вполне уместен в сообщениях.
Если вдруг по коду вырисовывается какая-то ситуация, ну которая вот точно никогда не произойдёт, такое иногда делают для некоторой завершённости или просто для ясности в будущем (я так иногда завершаю if...elif, добавляя ещё и else, чтоб мозг не выкипал) или если метод сделать на всякий случай (что вобщем-то чушь, есть абстрактные классы) то можно вставлять `raise NotImplementedError('Вразумительное сообщение')` чтобы вляпаться как следует и разобраться быстро.
А по поводу "вседозволенности" Python - мы все разумные люди и можем научиться брать эту его фичу под контроль. А если не можем, то есть много других хороших профессий. На разработке свет клином не сошёлся.
На текущий момент, самое успешное что я знаю из подобного, это connexion. У него много проблем, в том числе и та, что он сам по себе проблема, но он наиболее близок к решению подобной задачи.
Очередное нежелание глубоко освоить инструмент. Просто опишите спеку на OpenAPI и всё. Изменение кода будет требовать изменений в спецификацию и в таком виде. И это будет в разы сложнее. Код захламлён, описание спеки усложняется постоянным дублированием. Плюс к этому нужно втащить в проект минимум два модуля дополнительных.
За идею разместить на гитхабе — спасибо.
Здесь могут быть некоторые разночтения, но лично я пишу юнит-тест на функцию или метод класса, этот уровень мне кажется приемлемым.
Безусловна вся программа это собрание зависимых между собой методов и функций. И тут важную роль играет тестовый инструментарий. Например тот же pytest позволяет создавать своеобразные каскады тестовых данных. Это когда ты создаёшь фикстуры используя в качестве родителей другие фикстуры. И вопросы зависимостей решаются хорошо. Вот класс А он ни от кого не зависит (ну или с некоторым отступлением позволяет себя так рассматривать), вот класс Б он зависит от класса А.
Создаём тесты на класс А.
Создаём фикстуру, которая возвращает класс А.
Создаём тесты на класс Б в которых используем фикстуру с А.
Создаём фикстуру, которая возвращает класс Б.
Создаём тесты на класс А, для ситуации когда ему нужен Б. И применяем имеющиеся фикстуры.
Далее, берём задачу и думаем, что нам надо. Нам надо изменить класс А, каким образом? Добавить функционал или провести рефакторинг? Соответственно работаем. Добавляем в тесты и фикстуры изменения, чтоб получался ожидаемый результат.
Пускаем тест, не пашет. Берём лопату и идём копать пока не отработает ожидаемо.
Зачастую это не так. Пишешь-пишешь, оппа, оказывается есть такой подводный камень и пол решения надо переделывать, включая интерфейс взаимодействия с внешним миром. Половина тестов к коту под хвост.
Или вот про зависимости. Начали по ТДД, раз тест, два тест, три. А потом добавили зависимость — и все предыдущие тесты надо поправить. Потом снова. А затем переосмыслил подход и избавился от зависимости — снова меняем ранее написанные тесты.
Такое происходит тогда, когда меняешь интерфейс взаимодействия с внешним миром.
Если ты что-то переосмыслил и у тебя полетел тест, значит тут или исправлен баг или изменилась бизнес-логика.
Для меня лично применение практик TDD, стало обыденностью. Но нужно понимать, что тот же господин Бек, решает в первую очередь вопросы собственного рабочего дискомфорта. Например его онанизм на зелёную полоску это просто медитативная техника. Многие для того же используют ручное форматирование кода или попивание кофейка — сделал глоточек (поставил пробельчик) — обдумал — ещё глоточек (пробельчик). И всё в том духе.
На мой субъективный взгляд, основная ошибка применения TDD это забывчивость о задаче проектирования решения, собственно поставленной задачи. Мы НЕ движемся маленькими шагами, мы следуем техпроцессу. А тест это и есть техпроцесс, причём пошаговый техпроцесс. В различных инженерных отраслях техпроцесс уже больше сотни лет норма. Отличие лишь в том, что пишутся огромные талмуды специальными людьми, а программист будет писать небольшой локальный техпроцесс, для себя. Никто из химиков, машиностроителей и прочих, не бежит заглядывать в ректификационную колонну или выдёргивать из автомобиля мост, чтоб посмотреть как оно ДОЛЖНО работать. Он читает техпроцесс. А токарь не смотрит как эта самая машина едет мимо, видит мост и делает также. Он берёт техпроцесс и делает деталь согласно ему.
То что разработчик имеет лёгкий доступ к коду программы и сильное дробление на отдельные задачи и подзадачи, не отменяет тот факт, что принципы его работы глобально не отличаются от других инженерных специальностей. Просто они имеют некоторые особенности.
Тест помогает осознать последовательность шагов, применяемые решения и узкие места. А также показать, что и с каким результатом сделано. Не нужно применять его как медитативный инструмент, или вам перекрыли доступ к кофе?
В моём пет-проекте используется EAV. Наелся этого по самое нихочу.
Автор ничтоже сумняше не раскрыл один момент. Трёхтабличный EAV это не более чем пример практики, в реальности что-то адекватное можно построить не менее чем на 7. В моём проекте эта схема реализована на 9 и планирую расширить до 12 в ближайшей перспективе.
Благодаря этому я сохранил преимущества EAV плюс добился того что атрибуты хранятся в БД в своём типе, а не в строке. Недостатки остались конечно.
Вопрос производительности остаётся открытым и стоит остро. Метрики этого пока не собираю.
Тяжеловатая статья. Тем кто собирается изучить SQLAlchemy предлагаю начать с Мега-учебника Гринберга и после сразу переключиться на официальную документацию.
По моему мнению декларативное описание несколько чудное и отношения раскрыты недостаточно (да я видел слово «основы» в названии).
Непонятно почему используется VARCHAR, а не String.
Есть такой фильм «Человек который изменил всё». Так вот там была цитата про 50 метров г… на. Рекомендую просмотреть и оценить весь её шик.
Если применить подобный алгоритм, то получится специалист о котором заунывно взывают статьи подобные этой:
1. Высшее образование получать нужно. Лучше даже два. Первое какое-либо техническое, второе IT на базе технического (нужно будет отучиться 3 года вместо 5 в сумме 8 лет).
2. Устроиться на должность максимально близкую к ИТ, в идеале стажёром на погроммиста.
3. Пахать 10 часов в день, каждый день, без выходных и по 5 часов в праздники.
4. Учиться, учиться и ещё раз учиться.
5. Повтори пункт 4.
Не рекомендую тратить много сил на муки выбора, лучше всё пробовать и всем заниматься. Когда поймешь, что есть что в общих чертах, сможешь сам решить, в каком направлении надо двигаться, а от какого лучше отказаться.
Сергей, младший программист
Золотые слова!
А ещё я бы посоветовал ставить себе достижимые цели и верить в себя. А всё остальное придёт.
Я тоже админил больше 10 лет. Курсов не заканчивал. Через 4 года стал миддлом. Учавствовал в 2 довольно больших проектах, сейчас в третьем и делаю свой маленький веб-сервис. И что ж я теперь не могу честно сказать, что я разработчик среднего уровня?
Не боитесь размывания целеустремления сотрудника? Всё-таки его задача разработка и получение продукта определённой готовности. По моему опыту, когда ты и швец, и жнец, и ещё за кадровика тащишь 100% собеседования, начинается выпадение из твоего собственного рабочего русла. Как итог ты везде и нигде, плюс на тебе ответственность за принятые решения за которые приходится отвечать, вместо того, чтобы писать код.
Не совсем согласен, в реальности у нас происходит сильное уменьшение объёмов тестирования, разделение тестов да и просто включение логики - что имеет смысл тестировать.
Например позитивные и негативные тесты уже серьёзно уменьшают эту цифру - если мы используем целочисленное поле, то проверить его на входе более чем 3 позитивными параметрами не имеет смысла. Негативные сценарии это уже вопрос валидации, если же её нет, то это весьма значимый вопрос к проектированию кода - где она? По факту любое непопадание в валидацию сводит на нет попарное тестирование, так как при любой из комбинаций если хоть одно поле не валидируется, то у нас идёт отказ на обработку. И здесь мы просто проходимся по каждому полю индивидуальным тестом уменьшая объём передаваемых параметров только до тех которые реально дают понимание проблемы или ситуации.
Ещё ситуация которая эту историю сильно разгружает - обязательные поля. Хоть одно обязательное поле есть всегда, чаще их от 3 до 5. То есть фактически мы уменьшаемся по количеству комбинаций на десятки процентов. И всё это вместе даёт по моему опыту 300-1500 позитивных комбинаций, плюс штук 20-30 тестов на негативные истории.
Если честно, то я считаю, что ручное тестирование, которое дублирует автоматизацию это скорее поражение, так как стремятся тестировать руками всё подряд вместо тщательного контроля того, что недоступно автоматизации. Например при выборе правильного интерфейса тестирования, мы можем процентов на 99 закрыть автоматизацией тестирование API, но тестировщики руками упорно продолжают слать строки в целочисленные поля. Или ещё хуже тестируют руками бекенд через фронтенд. И как итог вместо того, чтобы добавить к автоматизации кейсы ручного тестирования, задачи дублируются. На самом деле тут причина не в том, что специалисты тестирования тупые, а в том, что системы очень часто не проектируются под возможность тестирования. И яркое этому подтверждение появление такой позиции как SDET. Так как приходится ещё и в разработке шарить, чтобы создать дополнительную нашлёпку, чтобы автотесты вобще возможны были.
Был рад увидеть ваш комментарий. После отправки своего комментария, я решил было, что занимаюсь некромантией.
Не вижу никаких проблем в полном переборе, pairwise будет просто подмножеством и всё. Потом потребуется 3wise, 4wise и тп. Так что смысл уходить от этого по факту теряется. Сложности будут если поля взаимоисключающие, но тут скорее вопрос почему вообще возникла эта ситуация? В крайнем случае можно сделать немного грязно, если взаимоисключающие поля есть, то сделать список исключений для этих пар полей и просто в тесте его пропускать, а сам список отдать в другой тест, чтобы этот список исключений мучить там.
Если честно, для обязательных полей у меня есть решение, их достаточно передавать в параметризации как словарь или список, а в теле теста просто распаковать, и собрать в общую кучу с остальными.
На самом деле тестировщик я довольно своеобразный, я применяю TDD и это сказывается на подходах к тестированию.
Pytest вполне умеет декартово произведение. Да и в целом вымучивать из себя промты когда задача имеет математическое решение токое себе.
Не совсем понятно что делать если часть полей обязательные, а часть нет. В целом статья бесполезная.
Хорошая статья, нет правда, всегда радует, что кто-то думает схоже.
Попробую немного добавить по некоторым моментам.
Паттерн Фабрика - мне кажется он несколько недооценён в Питоне по причине того, что мы пытаемся использовать его слишком развёрнуто. Думаю, что имеет смысл добавить немного простоты, если фабрику оформить как функцию, в которую аргументом ты кидаешь класс описывающий конфигурацию и затем она собирает в единый объект всё приложение возвращая его, то получается весьма понятно и гибко. Конечно вероятность того, что нам понадобится миллион экземпляров приложения с чуть-чуть разными параметрами нет так много. Но всё же есть и эту вероятность закрывают тесты - так мы можем вариативно тестировать приложение. Например, банально отключая какую-то фичу и прогоняя весь набор тестов (несколько искусственный пример). Если кто использует pytest очень рекомендую фикстуры-фабрики.
Ошибки и исключения - Хочу добавить, что имеет смысл построить дерево исключений в самой верхушке которого будет что-то типа
MySuperAppException(Exception), а все остальные исключения наследовать от него и давать логичные названия. И взять за правило, что эти два слова будут только в одном месте проекта. Тогда перехватить всё подряд будет значительно сложнее. Ещёrepr(e)в некоторых ситуациях может быть вполне уместен в сообщениях.Если вдруг по коду вырисовывается какая-то ситуация, ну которая вот точно никогда не произойдёт, такое иногда делают для некоторой завершённости или просто для ясности в будущем (я так иногда завершаю if...elif, добавляя ещё и else, чтоб мозг не выкипал) или если метод сделать на всякий случай (что вобщем-то чушь, есть абстрактные классы) то можно вставлять `raise NotImplementedError('Вразумительное сообщение')` чтобы вляпаться как следует и разобраться быстро.
А по поводу "вседозволенности" Python - мы все разумные люди и можем научиться брать эту его фичу под контроль. А если не можем, то есть много других хороших профессий. На разработке свет клином не сошёлся.
Очередное нежелание глубоко освоить инструмент. Просто опишите спеку на OpenAPI и всё. Изменение кода будет требовать изменений в спецификацию и в таком виде. И это будет в разы сложнее. Код захламлён, описание спеки усложняется постоянным дублированием. Плюс к этому нужно втащить в проект минимум два модуля дополнительных.
За идею разместить на гитхабе — спасибо.
Безусловна вся программа это собрание зависимых между собой методов и функций. И тут важную роль играет тестовый инструментарий. Например тот же pytest позволяет создавать своеобразные каскады тестовых данных. Это когда ты создаёшь фикстуры используя в качестве родителей другие фикстуры. И вопросы зависимостей решаются хорошо. Вот класс А он ни от кого не зависит (ну или с некоторым отступлением позволяет себя так рассматривать), вот класс Б он зависит от класса А.
Далее, берём задачу и думаем, что нам надо. Нам надо изменить класс А, каким образом? Добавить функционал или провести рефакторинг? Соответственно работаем. Добавляем в тесты и фикстуры изменения, чтоб получался ожидаемый результат.
Пускаем тест, не пашет. Берём лопату и идём копать пока не отработает ожидаемо.
Такое происходит тогда, когда меняешь интерфейс взаимодействия с внешним миром.
Если ты что-то переосмыслил и у тебя полетел тест, значит тут или исправлен баг или изменилась бизнес-логика.
На мой субъективный взгляд, основная ошибка применения TDD это забывчивость о задаче проектирования решения, собственно поставленной задачи. Мы НЕ движемся маленькими шагами, мы следуем техпроцессу. А тест это и есть техпроцесс, причём пошаговый техпроцесс. В различных инженерных отраслях техпроцесс уже больше сотни лет норма. Отличие лишь в том, что пишутся огромные талмуды специальными людьми, а программист будет писать небольшой локальный техпроцесс, для себя. Никто из химиков, машиностроителей и прочих, не бежит заглядывать в ректификационную колонну или выдёргивать из автомобиля мост, чтоб посмотреть как оно ДОЛЖНО работать. Он читает техпроцесс. А токарь не смотрит как эта самая машина едет мимо, видит мост и делает также. Он берёт техпроцесс и делает деталь согласно ему.
То что разработчик имеет лёгкий доступ к коду программы и сильное дробление на отдельные задачи и подзадачи, не отменяет тот факт, что принципы его работы глобально не отличаются от других инженерных специальностей. Просто они имеют некоторые особенности.
Тест помогает осознать последовательность шагов, применяемые решения и узкие места. А также показать, что и с каким результатом сделано. Не нужно применять его как медитативный инструмент, или вам перекрыли доступ к кофе?
В моём пет-проекте используется EAV. Наелся этого по самое нихочу.
Автор ничтоже сумняше не раскрыл один момент. Трёхтабличный EAV это не более чем пример практики, в реальности что-то адекватное можно построить не менее чем на 7. В моём проекте эта схема реализована на 9 и планирую расширить до 12 в ближайшей перспективе.
Благодаря этому я сохранил преимущества EAV плюс добился того что атрибуты хранятся в БД в своём типе, а не в строке. Недостатки остались конечно.
Вопрос производительности остаётся открытым и стоит остро. Метрики этого пока не собираю.
Миграции данных делаешь сам.
По моему мнению декларативное описание несколько чудное и отношения раскрыты недостаточно (да я видел слово «основы» в названии).
Непонятно почему используется VARCHAR, а не String.
Если применить подобный алгоритм, то получится специалист о котором заунывно взывают статьи подобные этой:
1. Высшее образование получать нужно. Лучше даже два. Первое какое-либо техническое, второе IT на базе технического (нужно будет отучиться 3 года вместо 5 в сумме 8 лет).
2. Устроиться на должность максимально близкую к ИТ, в идеале стажёром на погроммиста.
3. Пахать 10 часов в день, каждый день, без выходных и по 5 часов в праздники.
4. Учиться, учиться и ещё раз учиться.
5. Повтори пункт 4.
Золотые слова!
А ещё я бы посоветовал ставить себе достижимые цели и верить в себя. А всё остальное придёт.