Как ускорить UI тестирования используя AWS Lambda

Предыстория предыстории :slight_smile:

Проект, на котором я работаю, использует инфраструктуру Amazon. И ,поэтому, мне самому была интересна эта статья, а информацию найденную в этой статье я попробую реализовать на собственном примере используя #webdriver + #python . Но пока решил перевести , вот сама статья.

Предыстория:

Blackboard является мировым лидером в предоставлении надежного и инновационного образовательного ПО и услуг для клиентов в сфере высшего образования. Команда работающая по всему миру, по крайней мере в 10 разных часовых поясах. Мы используем Selenium Webdriver для кросс-браузерной автоматизации с 2007 года. Поскольку мы сейчас практикуем continuous delivery, UI автоматизация тестирования помогает в загруженом графике деплоев. Кроме того, каждый комит,запускает прогон тестов. Если вы когда-либо внедряли автоматизированную инфраструктуру тестирования UI, вы знаете, что очень сложно масштабировать и поддерживать. Хотя есть сторонние сервисы, которые полезны для тестирования различных комбинаций браузера / ОС, они не соответствуют нашим потребностям в масштабируемости.

Синхронный запуск тестов проходил 3 часа, что показало очевидную необходимость параллельного запуска. Ранее мы использовали Mesos для организации контейнера Selenium Grid Docker для каждого тестового прогона. Таким образом, мы смогли запустить восемь одновременных потоков для выполнения теста, что занимало в среднем 16 минут. Переход за пределы 8 одновременных сеансов в одном контейнере привел к проблемам производительности, часто возникают проблемы в Webdriver или с браузером. Мы попробовали Mesos и рассмотрели Kubernetes для управления докер контейнерами Selenium Grid , но это не помогло. И мы обратились к AWS Lambda.

Решение

Мы начали использовать AWS Lambda для тестирования UI, потому что он не требует дорогостоящей инфраструктуры или множества человеческих часов для обслуживания. Шаги, описанные в этом блоге, заняли один рабочий день, от момента создания до внедрения. Просто упаковывая набор тестов в функцию лямбда, мы можем проводить эти тесты параллельно и масштабировать. Мы используем JUnit, который вызывает функцию Lambda с запросом на запуск отдельно каждого теста из тест сьюта. Затем он агрегирует результаты, возвращенные при каждом выполнении теста Lambda.

Запуск Chrome в лямбда

(На момент написания статьи) Chrome для Linux не будет работать в Lambda. В запуске использовался Chromium с небольшой модификацией. Потребовалось около двух часов, чтобы собрать нужную нам ветку Chromium.

Необходимые библиотеки

Нам нужно включить некоторые дополнительные библиотеки, чтобы заставить Chrome и ChromeDriver работать. Все ресурсы java во время компиляции включено в базовый каталог скомпилированного файла jar. Когда этот файл jar развернут в Lambda, он помещается в каталог / var / task /. Это позволяет просто размещать библиотеки в папке lib /.

Чтобы установить эти библиотеки - просто создайте EC2 и выберите AMI Amazon Linux.


Затем используйте ssh для подключения к серверу. И ищем путь к библиотекам.

sudo find / -name libgconf-2.so.4
sudo find / -name libORBit-2.so.0

Теперь копируем эти файлы из EC2 в папку ресурсов java под lib /.

“Упаковка тестов”

Чтобы развернуть набор тестов в Lambda, мы использовали простой инструмент ShadowJar , который похож на Maven Shade Plugin. Чтобы включить зависимости, добавьте эту строчку в файл build.gradle.

shadowJar { from sourceSets.test.output configurations = [project.configurations.testRuntime] }

Развертывание тестов

Теперь нам нужно запустить их в текущую функцию лямбда. Мы используем простые шаблоны SAM для загрузки их в S3, а затем разворачиваем ее в Lambda с нашими настройками.

json

{ "AWSTemplateFormatVersion": "2010-09-09", "Transform": "AWS::Serverless-2016-10-31", "Resources": { "LambdaTestHandler": { "Type": "AWS::Serverless::Function", "Properties": { "CodeUri": "./build/libs/your-test-jar-all.jar", "Runtime": "java8", "Handler": "com.example.LambdaTestHandler::handleRequest", "Role": "<YourLambdaRoleArn>", "Timeout": 300, "MemorySize": 1536 } } } }

Мы используем максимальный тайм-аут, чтобы наши тесты имели достаточно времени для запуска. Мы также используем максимальный размер памяти, поскольку это гарантирует, что наша функция Lambda может поддерживать Chrome и другие ресурсы, необходимые для запуска UI тестов.

Указание обработчика важно, потому что этот класс выполняет требуемый тест. Обработчик теста должен иметь возможность получать тестовый класс и метод. С помощью этой информации он выполнит тест и возвращает нам результаты.

public LambdaTestResult handleRequest(TestRequest testRequest, Context context) { LoggerContainer.LOGGER = new Logger(context.getLogger()); BlockJUnit4ClassRunner runner = getRunnerForSingleTest(testRequest); Result result = new JUnitCore().run(runner); return new LambdaTestResult(result); }

Создание совместимого с Lambda ChromeDriver

Когда мы запускаем тесты на AWS, мы настроили ChromeDriver для запуска их в Lambda.

Чтобы настроить ChromeDriver, сначала нужно сообщить ChromeDriver, где можно найти бинарный файл Chrome. Поскольку мы знаем, что ChromeDriver будет распакован в корневой каталог , мы должны указать конфигурацию ChromeDriver в этом месте.

Настройки для запуска ChromeDriver в основном связаны с Chrome, которые должны иметь рабочие каталоги, указывающие на папку tmp /.

Начните с Default DesiredCapabilities для ChromeDriver, а затем добавьте следующие параметры, чтобы включить ChromeDriver в Lambda.

public ChromeDriver createLambdaChromeDriver() { ChromeOptions options = new ChromeOptions(); // Set the location of the chrome binary from the resources folder options.setBinary("/var/task/chrome"); // Include these settings to allow Chrome to run in Lambda options.addArguments("--disable-gpu"); options.addArguments("--headless"); options.addArguments("--window-size=1366,768"); options.addArguments("--single-process"); options.addArguments("--no-sandbox"); options.addArguments("--user-data-dir=/tmp/user-data"); options.addArguments("--data-path=/tmp/data-path"); options.addArguments("--homedir=/tmp"); options.addArguments("--disk-cache-dir=/tmp/cache-dir"); DesiredCapabilities desiredCapabilities = DesiredCapabilities.chrome(); desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, options); return new ChromeDriver(desiredCapabilities); }

Параллельное выполнение тестов

Вы можете выполнить параллельное выполнение теста в Lambda разными способами. Ваш подход зависит от структуры и архитектуры ваших тестов. Для нашего решения мы внедрили собственный тестовый 'runner', который использует библиотеки JUnit для создания списка тестов, которые мы хотим запустить. Когда у нас есть список, мы создаем объект TestRequest для перехода к функции Lambda, которую мы развернули. В этом TestRequest мы помещаем имя класса, метод тестирования и идентификатор тестового прогона. Когда функция Lambda получает этот TestRequest, наш LambdaTestHandler генерирует и запускает тесты JUnit. По завершении -результат теста отправляется на тестовый 'runner', который компилирует результат после завершения всех тестов. Выполняя одну и ту же функцию Lambda несколько раз с различными запросами, мы можем эффективно запустить весь набор тестов параллельно.

Чтобы получить скриншоты и другие тестовые данные, мы передаем эти файлы во время выполнения теста в S3. Когда тесты завершены, мы связываем файлы с каждым исполнением теста в отчете, созданном в тестах. Это позволяет нам легко проверить логи и тестовые артефакты.



AWS Lambda имеет предел 250 МБ несжатого пространства для упакованных функций. Поскольку у нас есть библиотеки и другие зависимости к нашему набору тестов, мы достигаем этого предела, когда пытались загрузить функцию, содержащую Chrome и ChromeDriver (~ 140 МБ). Этот набор тестов изначально не предназначался для использования с Lambda. Чтобы обойти этот предел, мы использовали временную директорию функций Lambda, которая позволяет во время выполнения использовать до 500 МБ пространства. Загрузка этих файлов во время выполнения тестов перемещает часть пространства во временный каталог. Это предоставляет больше места для библиотек и зависимостей.

json

private static void downloadS3ObjectToExecutableFile(String key) throws IOException { File file = new File("/tmp/" + key); GetObjectRequest request = new GetObjectRequest("s3-bucket-name", key); FileUtils.copyInputStreamToFile(s3client.getObject(request).getObjectContent(), file); file.setExecutable(true); }

Выводы

Год назад один из наших наборов тестов занимал несколько часов. В прошлом месяце это заняло 16 минут. Сегодня это занимает 39 секунд . Благодаря AWS Lambda мы можем сократить время сборки максимально возможно.

8 лайков

Офигенно.
Вопрос - мы упирались в производительность самого site-under-test, на тестовых энвах тазики слабые, и начинали плохо отвечать браузерам уже при 50 тредах. Вы просто нарастили мощность железа на тестовых окружениях или что то хитрее?

И еще, сколько в итоге получилось одновременных тестов? Все? Или какой то пул таки есть?

Это перевод. Я думаю переводчик ещё не делал у себя этого. Но если делал то ответит. На сколько я помню, лямбда машина в АВСе это не такое и слабое железо, но очень ограниченный жесткий диск. Выгода в том что стоит копейки, в прямом смысле копейки. По-этому их можно развернуть штук 200 на один прогон заплатив за него 2-3 евро. И при распаралеливании тестов каждая машина выполнит по 2-3 теста, так что прогон займет 15-20 секунд для всего тест сета (1 тест на машину).
Автор поправит меня, если я снова ляпнул что-то несуразное.

1 лайк

Отличная статья, спасибо!
У меня как раз задача развернуть автоматизацию тестирования с нуля. Наши сервера в AWS и я тоже хотел бы использовать #webdriver + #python.
Пока, правда, понятия не имею с чего начать.

2 лайка