company_banner

Оптимальная параллелизация юнит-тестов или 17000 тестов за 4 минуты

    Сегодня мы поговорим про разработанную нами утилиту, которая оптимизирует тестирование PHP-кода с помощью PHPUnit и TeamCity. При этом нужно понимать, что наш проект — это не только веб-сайт, но и мобильные приложения, wap-сайт, Facebook-приложение и много чего ещё, а разработка ведется не только на PHP, но и на C, C++, HTML5 и т.д.

    Методы, которые мы описываем, прекрасно адаптируются под любой язык, любую систему тестирования и любое окружение. Поэтому наш опыт может оказаться полезным не только разработчикам веб-сайтов на PHP, но и представителям других областей разработки. Кроме того, в ближайшем будущем мы планируем перевести нашу систему в Open Source ― без обязательной привязки к TeamCity и PHPUnit ― наверняка она кому-нибудь пригодится.

    Зачем это нужно?


    Юнит-тестирование ― обязательная (мы на самом деле в это верим!) составляющая любого серьёзного проекта, над которым трудятся десятки людей. И чем больше проект, тем больше в нём юнит-тестов. Чем больше юнит-тестов, тем больше время их выполнения. Чем больше время их выполнения, тем больше разработчиков и тестировщиков принимают решение «мягко игнорировать» их запуск.

    Естественно, это не может положительно сказаться на качестве и скорости тестирования задач. Тесты полноценно прогоняются только перед релизом, в последнюю минуту начинаются выяснения в духе «А почему упал вот этот тест?», и зачастую всё сводится к «А давайте разложимся так, а потом будем чинить!». И хорошо ещё, если проблема в тесте. Гораздо хуже, если она в тестируемом коде.

    Лучший выход из такой ситуации ― сократить время выполнения тестов. Но каким образом? Можно при тестировании каждой задачи запускать только тесты, покрывающие затрагиваемый ею код. Но и это вряд ли даст 100% гарантии, что всё в порядке: ведь в проекте, состоящем из многих тысяч классов, иногда трудно проследить тонкую связь между ними (юнит-тесты, если верить книжкам, НИКОГДА не должны выходить за пределы тестируемого класса, а ещё лучше ― тестируемого метода. Но много ли вы видели таких тестов?). Следовательно, обязательно нужно запускать ВСЕ тесты параллельно, в несколько потоков.

    Самое простое решение для неленивых ― это, например, руками создать несколько конфигурационных XML-файлов PHPUnit и запускать их отдельными процессами. Но хватит его ненадолго: нужна будет постоянная поддержка этих конфигов и повысится вероятность упустить какой-нибудь тест или целый пакет, да и время выполнения будет далеко не оптимальным.

    Самое простое решение для ленивых ― написать простенький скрипт, который будет делить тесты поровну между потоками. Но в какой-то момент и оно себя исчерпает, потому что количество тестов постоянно растёт, все они работают с разной скоростью, из чего возникает и разница по времени выполнения среди потоков. Следовательно, нужны более эффективные и контролируемые методы борьбы с ними.

    Вывод: нам нужна простая в управлении система, которая будет автоматически и максимально равномерно распределять тесты по потокам и самостоятельно контролировать покрытие кода тестами.

    Поиск


    Поскольку PHPUnit ― очень распространённая система, мы подумали, что наверняка кто-то уже что-то подобное сделал, и начали искать.

    Первые результаты не разочаровали: нашлось множество решений разной степени готовности и уровня функциональности. Это были и bash-скрипты, и PHP-скрипты, и обёртки вокруг PHPUnit, и даже достаточно сложные и размашистые патчи для самой системы. Мы стали пробовать некоторые из них, копаться в коде, пытаться адаптировать под наш проект и столкнулись с огромным количеством проблем, неоднозначностей и логических ошибок. Это было удивительно: неужели до сих пор нет ни одного стопроцентно проверенного решения?

    Большая часть вариантов была построена по одной из двух схем:

    • Ищем все необходимые файлы и делим их поровну между потоками;
    • Запускаем некоторое количество потоков, которые последовательно обрабатывают общую очередь тестов и «скармливают» их PHPUnit.

    Недостаток первого способа очевиден: тесты распределяются не оптимальным образом, что, конечно, повышает скорость их работы, но зачастую какой-нибудь поток работает в 3-4 раза дольше всех остальных.

    Недостаток же второго способа уже не так очевиден и связан с интересными особенностями внутреннего устройства PHPUnit. Во-первых, каждый запуск процесса PHPUnit сопрягается со множеством действий: эмуляцией тестового окружения, сбором информации о количестве и структуре тест-классов и так далее. Во-вторых, при запуске большого количества тестов PHPUnit достаточно хитрым и умным образом собирает все дата-провайдеры для всех тестов, минимизируя количество обращений к дискам и базам данных (повторимся: не бывает совсем-совсем правильных юнит-тестов!), но это преимущество теряется, если выдавать системе тесты по одному. В нашем же случае этот способ вызывал огромное количество необъяснимых ошибок в тестах, отловить и повторить которые было невероятно сложно, потому что при каждом запуске практически всегда менялась последовательность тестов.

    Ещё мы нашли информацию, что разработчики PHPUnit хотят реализовать поддержку многопоточности «из коробки». Но проблема в том, что этим обещаниям уже не один год, комментарии по поводу развития проекта скупы и никаких даже приблизительных прогнозов нет, так что рассчитывать на это решение пока что очень рано.

    Потратив несколько дней на изучение всех этих вариантов, мы пришли к выводу, что ни один из них не решает полностью поставленную нами задачу и нужно писать что-то своё, максимально адаптированное к реалиям нашего проекта.

    Муки творчества


    Самая первая идея лежала на поверхности. Мы определили, что большая часть тест-классов работает приблизительно равное время, но есть и такие, которые выполняются в десятки и сотни раз дольше. Поэтому мы решили разделить эти два вида тестов (именно самостоятельно определяя эти «медленные» тесты) и прогонять их отдельно друг от друга. Например, все «быстрые» тесты провести в первых пяти потоках, а все «медленные» ― в трёх других. Результат оказался достаточно неплохим, при том же количестве потоков тесты проходили приблизительно в два раза быстрее, чем с двухпоточным вариантом, но разброс по времени работы разных потоков всё же был достаточно ощутим. Следовательно, нужно собирать информацию по времени работы тестов, чтобы распределять их максимально равномерно.

    Следующая мысль, которая пришла к нам в голову, ― сохранять время работы тестов после каждого запуска, а потом использовать эту информацию для последующего комбинирования. Тем не менее, у этой идеи тоже были недостатки. Например, чтобы статистика была доступна отовсюду, её нужно было сохранять во внешнее хранилище ― например, базу данных. Отсюда две проблемы: во-первых, ощутимое время выполнения этой операции, во-вторых, возникали бы конфликты при обращении к этому хранилищу нескольких пользователей одновременно (конечно, они решаются использованием транзакций, локов и т.д., но мы всё это делаем ради скорости, а любое разрешение конфликтов тормозит процесс). Можно было бы сохранять данные по какому-то особому условию или с определённой периодичностью, но это всё исключительно «костыли».

    Из этого следовало, что нужно собирать статистику в определённом изолированном месте, куда бы все обращались только за чтением, а запись производилась бы чем-то отдельным. Именно в этот момент мы вспомнили о TeamCity, но об этом несколько позже.

    Существует ещё одна проблема: время выполнения тестов всегда может колебаться как локально ― например, при резком падении производительности серверов, так и глобально ― когда на сервера повышается или понижается нагрузка (тесты у нас проводятся на тех же машинах, на которых ведётся разработка, и запуск разработчиком любого ресурсоёмкого скрипта заметно влияет на время работы тестов). Значит, для максимальной актуализации статистики её нужно накапливать и использовать усреднённое значение.

    Каким же образом конфигурировать эту утилиту? При первой реализации всё управление осуществлялось параметрами командной строки. То есть мы запускали утилиту с параметрами для PHPUnit, а она передавала их в запускаемые процессы и добавляла свои ― например, полный список запускаемых тестов (и восхитительные строки запуска PHPUnit были длиной в тысячи символов). Понятно, что это неудобно, заставляет лишний раз задумываться о передаваемых параметрах и способствует появлению новых ошибок. После множества экспериментов наконец-то пришло самое простое решение: можно использовать стандартные конфигурационные XML-файлы PHPUnit, меняя настройки в них и добавив несколько собственных XML-тегов в структуру. Таким образом, с помощью этих конфигурационных файлов можно будет запускать как чистый PHPUnit, так и нашу утилиту с одним и тем же результатом (не учитывая, конечно, прироста скорости в нашем случае).

    Решение


    Итак, спустя многие часы планирования, разработки, поиска серьёзных багов и вылавливания незначительных, мы пришли к той системе, которая работает и сейчас.

    Наш Multithread Launcher (или, как мы его ласково называем, «пускалка») состоит из трёх изолированных классов:

    • Класс для сбора и сохранения статистики по времени выполнения тестов;
    • Класс для получения этой статистики и равномерного распределения тестов;
    • Класс для генерации конфигурационных файлов PHPUnit и запуска его процессов.


    Первый класс работает с TeamCity. Она регулярно запускает билд, который среди всего прочего прогоняет абсолютно все тесты, а затем включает наш сборщик статистики. У этого сотрудничества есть и преимущества, и недостатки. С одной стороны, TeamCity сама собирает статистику по всем запущенным тестам, с другой ― API TeamCity не предоставляет никаких средств для работы с этой информацией (ни родной, ни даже более развитый REST API), поэтому нам приходится обращаться напрямую к базе данных, с которой она работает.

    Здесь мы столкнулись с небольшой проблемой. Наша изначальная архитектура предусматривала файл с тест-классом как единицу информации для равномерного распределения тестов по потокам (так как это наиболее естественный для конфигурационных файлов PHPUnit способ). А TeamCity хранит статистику по отдельный тестам, никак не связывая их с файлами. Поэтому наш сборщик читает из базы статистику по тестам, соотносит тесты с классами, классы ― с файлами, и сохраняет статистику уже в таком виде.



    Это может показаться достаточно ресурсоёмким занятием, но мы написали простую систему кеширования соответствий «класс=>файл», и тяжёлая работа производится сборщиком только в случае появления новых классов, а их количество при каждом запуске не так велико.

    Статистика хранится в собственной базе данных в семи экземплярах ― за последние семь дней. Каждый день она собирается заново, но при распределении файлов используются данные за всю прошедшую неделю, причём чем новее статистика, тем выше её «вес» при расчёте среднего времени. Таким образом, единичное возрастание времени выполнения тестов не оказывает на статистику значительного влияния, а перманентные изменения учитываются достаточно быстро.

    Второй класс максимально простым алгоритмом формирует списки файлов для каждого потока. Он получает информацию из базы данных, сортирует все файлы по времени выполнения от большего к меньшему и распределяет их по потоком по принципу «каждый следующий файл в самый свободный поток». Благодаря тому, что тестов очень много, а маленьких гораздо больше, чем больших, ожидаемое время работы всех потоков отличается лишь на пару-тройку секунд (реальное, конечно, несколько больше, но разница все равно не слишком существенна).
    На картинке справа столбики — это потоки, каждый «кирпичик» — отдельный тест, высота которого пропорциональна времени прохождения.

    Третий класс занимается самым интересным. В основном режиме он принимает на вход стандартный конфиг PHPUnit, в котором указываются директории с тестами и конкретные файлы. В нём есть возможность пометить тест как «медленный» (время работы некоторых тестов сильно зависит от внешних факторов и их следует проводить отдельно от остальных) и «изолированный» (такие тесты запускаются в отдельных потоках после завершения всех остальных). Затем пускалка формирует собственный конфиг PHPUnit со всеми необходимыми параметрами вроде необходимого TestListener'а, а test suite'ы являются списками файлов для запуска в каждом потоке. Соответственно, после этого достаточно только запустить несколько процессов PHPUnit с этим конфигом и указанием необходимого test suite'а. Помимо основного режима существуют возможности для отладки тестов: запуск с самым последним или любым другим сгенерированным конфигом, со стандартным TestListener'ом PHPUnit вместо нашего, запуск тестов в разном порядке и т.д.

    К слову о наших TestListener'ах (это стандартный интерфейс PHPUnit, используемый для вывода информации о проведённых тестах). Изначально пускалка выводила информацию в таком же виде, в каком и PHPUnit по умолчанию. Но для большего удобства мы написали собственный Listener, который позволил сделать информацию более компактной и удобочитаемой, а также добавил новые возможности вроде перехвата STDOUT и STDERR.

    Плюс у нас есть TestListener для вывода информации в TeamCity в том виде, в котором она его ожидает (настолько страшном, что вес текстового отчёта об одном запуске в итоге достигает пяти мегабайт).



    Итоги


    Теперь немного цифр.
    Мы проводим больше 17 тысяч тестов в 8 потоков (плюс три дополнительных ― для «медленных» тестов, которые в нормальной ситуации проходят все вместе за минуту).
    В лучшем случае (при стандартной нагрузке тестовых серверов) тесты с помощью пускалки проходят за 3.5-4 минуты против 40-50 минут в один поток и 8-15 минут при распределении поровну.
    При сильно нагруженных серверах пускалка быстро приспосабливается к обстановке и отрабатывает за 8-10 минут. При распределении тестов поровну они работали не меньше 20-25 минут… И мы так ни разу и не дождались завершения работы тестов в один поток.



    В итоге мы получили систему, которая самостоятельно адаптируется под изменения нагрузки на серверах благодаря получаемой от TeamCity статистике, автоматически распределяет тесты по потокам и легко управляется и настраивается.

    Что дало нам такое ускорение выполнения тестов?
    • Во-первых, мы повысили скорость тестирования задач.
    • Во-вторых, из-за частого прогона тестов существенно понизилась вероятность поймать фатальные ошибки при массовом мерже веток задач в релизную ветку.
    • В-третьих, значительно разгрузили агентов TeamCity: тесты запускаются далеко не в одном билде, и теперь у них реально бывает свободное время на особые задачи от QA-инженеров.
    • В-четвёртых, стало возможным реализовать автоматический запуск тестов при отправке задачи на тестирование, так что тестировщик имеет некоторую информацию о работоспособности кода задачи уже при её получении.

    What's next?


    У нас есть ещё много идей и планов, как можно улучшить систему. Существует несколько известных проблем: иногда возникают сложности с изолированностью тестов, оставшихся со времён однопоточного запуска, пускалку пока нельзя перенести на другой проект в один клик ― так что поле для работы впереди ещё очень и очень большое, хотя система достигла довольно впечатляющих результатов уже сейчас.

    В ближайшее время (очень рассчитываем на июнь) будет произведён тотальный рефакторинг и пересмотр системы, чтобы её можно было превратить в проект Open Source.

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

    К тому же, архитектура системы изменится, чтобы у разработчиков была возможность без особых проблем отвязать пускалку от TeamCity и привязать её, например, к Jenkins, или работать исключительно локально.

    Не хочу сказать, что наш Multithread Launcher ― какая-то революция или новое слово в автоматизации тестирования, но в нашем проекте она показала себя с самой лучшей стороны и, даже несмотря на некоторые недостатки, работает значительно эффективнее всех прочих общедоступных решений.

    Илья Кудинов, QA-инженер.
    Badoo
    441,00
    Big Dating
    Поделиться публикацией

    Комментарии 31

      +2
      Странно, но по картинке с котиками я подумал что пост будет от компании Badoo.
        +5
        а от какой он компании?))
          +10
          от компании котиков :))
          +5
          У меня подобные изображения однозначно ассоциируются с задачей об обедающих философах.
            +2
            Картинка прямо под надписью, вы могли просто быстро пройти взглядом и прочитать, не осознав процесс. Или уловить боковым зрением.
            +3
            Вариант с Jenkins у вас не рассматривался?
              +1
              Когда начали разрабатывать пускалку уже была развитая инфраструктура с использованием TeamCity — так что изобретать велосипед не стали.

              Будем делать OpenSource — вполне вероятно, добавим модули и для Jenkins, если будет спрос (:
                +1
                спрос реально есть
                  +1
                  Судя по всему, это и будет первым доп. модулем)
                +1
                С полгода назад выполнял задачу распределения юнит тестов в связке Jenkins+NAnt+.NET+NUnit
                Решил не заморачиваться и разбил всю пачку на n категорий, и запускал тесты одной категории в один поток.
                было — порядка 10000 тестов за 70 минут на одной машине в один поток.
                стало — 10000 тестов за10-15 минут на трех машинах в два потока на каждой.
                  +1
                  Вероятность, что новый тест не попадёт ни в одну категорию и не будет запускаться у вас отсутствует?
                    +1
                    Для этого был предусмотрен отдельный поток — запуск всех тестов не входящих ни в одну из категорий. И в момент, когда выполнение этой пачки начинает занимать время, сравнимое с выполнением категории — можно запросто из них создать новую категорию. Поддерживая таким образом общее время выполнения в рамках допустимого.
                +1
                Вы не пробовали приспособить для этого билдовую систему Waf (https://code.google.com/p/waf/)? Она хоть и называется билдовой системой, но билды — это скорее побочный продукт ее функциональности.
                  +1
                  Задачей всё-таки было ускорить прохождение тестов, не вылезая из имеющейся инфраструктуры. Возможно, другие инструменты могли бы улучшить результат, но перекраивать имеющуюся мощную систему контроля качества только ради пускалки в наших планах не было
                    +1
                    А ваша пускалка разве не отдельная программа/скрипт? Какая разница в данном месте какой скрипт юзать?
                      +1
                      У нас уже использовалась билдовая система Ant (как я понимаю, Waf как раз его аналог), и его функционала вполне хватало — зачем использовать что-то дополнительно?
                        +1
                        Ладно, я понял, что не совсем понятно объяснил суть. Я не предлагал вам менять что-то в вашей инфраструктуре, я предлагал посмотреть на Waf как на вашу пускалку тестов и только.

                        Кстати вопрос, я может не нашел, но на чем написана ваша утилитка?
                          +1
                          Спасибо, теперь понял (: На самом деле, не натыкались на такую идею в процессе поиска решения — возможно, в противном случае всё могло пойти по-другому.

                          В принципе, большая часть трудов ушла именно на разработку системы сбора/хранения/использования статистики и распределения тестов по потокам — она ведь нигде и никак не реализована. А уж что использовать, чтобы запускать полученные потоки, уже дело отдельное — и это не такая большая работа, чтоб не написать это самому со 100% соответствиям потребностям системы.

                          Утилитка написана на PHP (:
                  +2
                  Очень круто. Множество коллег, с которыми я общался, не хотели пользоваться тестами как раз из-за времени выполнения. Ну и из-за необходимости их писать, понятное дело.
                    +2
                    Мы для .NET тестов написали приблуду, которая из тестовых проектов строит список тестовых классов и запускает параллельно копии консоля от nunit. Никак не доходят руки до реализации простейшего балансировщика.
                    Если кто посоветует готовое решение, мы бы велосипед не изобретали
                    Спасибо.
                      +1
                      Вот если бы в интернете было какое-нибудь общедоступное и более-менее универсальное решение, то нам бы не пришлось разрабатывать вышеописаный велосипед с моторчиком (:
                        +2
                        так что ждать nunit 3 с надеждой или загрузить новичка на работе. у нас вся эта программка писалось понемногу каждым новым «студентом» :)
                      +4
                      Так это же доклад с ритконфа!
                        +3
                        Каюсь, о пускалке рассказывал уже там, но тут подробнее и конструктивнее (:
                          +1
                          А, по-моему, в копейку :)
                          Но все равно, за статью большой +
                        –3
                        юнит-тесты, если верить книжкам, НИКОГДА не должны выходить за пределы тестируемого класса, а ещё лучше ― тестируемого метода. Но много ли вы видели таких тестов?

                        Какой кошмар. У меня на всех проектах всегда были именно такие модульные тесты. То, что вы таких тестов даже не видели меня шокировало.
                        Вероятно вам стоит посмотреть повнимательнее точно ли вы пишите юнит тесты и умеете ли вы их писать.
                        И тогда вам может не понадобятся такие навороченные инструменты которые вы тут предлагаете.

                        Если у вас 17 тысяч тестов это не нормально — таких сложных продуктов не должно быть — необходимо разбить его на отдельные модули и работать с многоуровневым тестированием.

                        В общем описанные в посте проблемы решаются обычно другими способами.
                          +1
                          Всё зависит от величины проекта. На тысячу строк 17 тысяч тестов может и перебор, но вот на миллион я бы сказал, что маловато.

                          И полностью изолировать тест класса и ли метода от других классов или методов редко представляется возможным малой кровью. Даже банальный сеттер setOwner(User $user) для какой-нибудь сущности требует создание экземпляра User стандартным способом (через new) или через какую-то фабрику типа loadUser, а значит о полной изоляции говорить нельзя, конструктор или фабрика тоже могут содержать ошибки и в своем тесте мы их неявно тестируем хотя бы на отсутствие fatal error. Использование моков, интерфейсов и т. п. лишь способ увеличить изоляцию, но не сделать её абсолютной — ошибки могут быть даже в интерфейсах.
                            –2
                            В таком случае незачем писать модульные тесты, если вы не можете архитектурно подготовить к этому систему. Существует большое количество других типов тестов.

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

                            Всё зависит от величины проекта. На тысячу строк 17 тысяч тестов может и перебор, но вот на миллион я бы сказал, что маловато.
                            Ничего не маловато — у меня были проекты более чем на миллион строк кода и хватило меньшего количества тестов при почти стопроцентном покрытии.
                            Вопрос в количестве бизнес логики которую имеет смысл тестировать. Логика специфичная для проекта на миллион строк кода? Это очень много. Я говорю о том, что имеет смысл разбить такой проект на отдельные проекты. Это как минимум. А вообще скорее всего поэтапно выделять модули, удалять их и заменять внешними решениями.
                            В реальности до описанного вами сценария дойти не должно — наверняка вы могли свернуть с этой дороги где то в другом месте. И текущая ситуация вызвана другой болезнью. И лечить надо ее а не симптомы.

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

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

                              Или ошибки в интерфейсах не будут выловлены, или изоляция не абсолютна.
                              Вопрос в количестве бизнес логики которую имеет смысл тестировать.

                              Кто сказал, что нужно тестировать только бизнес-логику?
                              Несмотря на то, что моя позиция не совпадает с вашей — это не повод сливать мне карму.

                              Упаси боже, за разногласия в технических вопросах я карму не сливаю.
                                –1
                                Или ошибки в интерфейсах не будут выловлены, или изоляция не абсолютна.
                                Мы можем считать изоляцию абсолютной считая интерфейс контрактом, который принадлежит тому кто его использует, а не тому, кто поставляет. Конечно модульный тест может быть изолирован только по отношению к другим модулям, но не по отношению к архитектуре взаимодействия модулей между собой. Полная изоляция означает что изменения в других модулях не повлияют на тесты — но изменения в архитектуре приложения конечно могут повлиять.

                                во вторых, тесты именно, что модульные
                                Вероятно у нас просто разная терминология.
                                Тесты делят на 3 типа по объему охвата. Модульные для тестирования модулей приложения, интеграционные для тестирования модулей в связке и системные для тестирования приложения как черного ящика.
                                В реальности под модульными тестами обычно понимают именно тестирование методов и классов.
                                Чем более низкий уровень тестирования используется на проекте — тем они дороже.
                                То есть если вы работаете с унаследованной архитектурой — модульные тесты наверняка не окупятся. Могут окупится некоторые интеграционные тесты, но каждую ситуацию стоит рассматривать отдельно.
                                Тут обычно нужны системные авто тесты или хорошо построенный процесс ручного тестирования. После этого можно будет опираясь на них провести архитектурный рефакторинг и подготовить систему к модульным тестам. Если приложение не будет эволюционировать дальше, то даже это может быть слишком дорогой мерой.

                                Если мы говорим о системных тестах — то 17 тысяч тестов это очень много — это может быть только для систем с очень сложным бизнесом. Чаще всего требования к продукту тоже можно упростить или забить на их часть менее важную для заказчика (то есть не тестировать эту часть).

                                Одним словом — с ситуацией у автора что то не чисто. Он ни словом не обмолвился о многоуровневом тестировании. Я видел проекты, в которых вместо организации тестирования как такового увлекались модульными тестами, которые писали тестировщики после написания кода. Это жалкое зрелище. Модульные тесты это все таки не тестирование — а часть девелопмента. Моя мысль сводится к тому, что может и предложенный инструмент хорош. Но в проектах ситуация требующая его применения возникнуть не должна. И вероятно стоит что то другое менять в проекте если к такому пришли.
                          0
                          Комментарий удален — глюк в браузере. Написан в верхней ветке

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое