Self-healing тесты и локальная LLM. llm.. llm. openai.. llm. openai. self-heal.. llm. openai. self-heal. автоматизация тестирования.. llm. openai. self-heal. автоматизация тестирования. тестирование.

В этой статье расскажу о практическом применении больших языковых моделей (LLM) в сочетании с традиционными инструментами автоматизации Python/Selenium для повышения надежности тестов.

Статья состоит из следующих разделов:
1. Что такое self-healing тесты 
2. Hardware конфигурация 
3. Software конфигурация 
4. Испытываем API локальной LLM 
5. Встраиваем в тесты 
6. Ограничения 
7. Перспективы

Что такое self-healing тесты

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

Исходя из встречаемых проблем будем понимать, что self-healing (самоисправляющиеся) тесты – это те, что могут автоматически адаптировать своё поведение в момент возникновения проблемы. Акцент текущей реализации сделан на решении проблемы 1.

Hardware конфигурация

В таблице ниже приведены измерения 2х параметров работы модели на наших конфигурациях и описание самих конфигураций.

1. MacMini 2023: 
GPU: Apple M2 10 ядер 
CPU: Apple M2 8(4+4) ядер 
RAM: 16GB

Результаты работы модели: 
TTFT – 35 секунд 
TPS – 18 токенов в секунд

2. MacBook Pro 2023:
   GPU: Apple M2 Pro 19 ядер 
   CPU: Apple M2 Pro 12(8+4) ядер 
   RAM: 16GB

Результаты работы модели:
TTFT: 18 секунд
TPS: 37 токенов в секунду

TTFT – time to first token (время, спустя которое пользователь начнёт получать ответ)
TPS – tokens per second (скорость получения ответа в токенах в секунду)

В силу ограниченного количества железа и опытной эксплуатации, используем только 1 сервер в первой конфигурации.

Software конфигурация

1. LMStudio https://lmstudio.ai/ – ”IDE” – удобно для отладки промптов, конфигурации параметров модели, оффлайн запуска и поднятия ”сервера моделей”

Отладка промптов

Отладка промптов
Настройка модели

Настройка модели
Настройка локального сервера
Настройка локального сервера

2. Модель lmstudio-community/Qwen2.5-7B-Instruct-MLX-4bit с HuggingFace (удобно скачать через LMStudio).
3. openai pip пакет.

Испытываем API локальной LLM

(venv) user@MacBook-Pro-Admin-2 web2 % cat tttt
from openai import OpenAI

LLM_URL = 'http://<IP_OF_SERVER_IN_YOUR_NETWORK>:1234/v1'
LLM_MODEL = 'qwen2.5-7b-instruct-mlx'

def call_llm(request):
    client = OpenAI(base_url=LLM_URL, api_key="lm-studio")
    completion = client.chat.completions.create(
        model=LLM_MODEL,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": request,
                    },
                ],
            },
        ],
    )
    return completion.choices[0].message.content

print(call_llm('Да или нет?'))
(venv) user@MacBook-Pro-Admin-2 web2 %
(venv) user@MacBook-Pro-Admin-2 web2 % python tttt
Вы задали вопрос, но не указали, что именно нужно ответить "да" или "нет" на. Пожалуйста, уточните свой вопрос, чтобы я мог помочь вам.

Встраиваем в тесты

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

Схема подключения LLM к тестам с PageObject.

Схема подключения LLM к тестам с PageObject.

Благодаря RLS (Run-time Locators Storage), все последующие тесты внутри тестового прогона не будут фейлиться на проблемном элементе. Важным также является организация design-time хранилища локаторов. Это отдельные классы, подключаемые к функциональным PageObject’ам. Выглядят примерно так:

class LLogin:
 
    @staticmethod
    def L_I18N_TEXTFIELD_LOGIN(lang=''):
        """поле ввода логина"""
        return ('xpath', f'//*[@e2e-id="I intentionally broke this locator"]')

Благодаря соглашению об именовании локаторов (имя начинается с префикса L_) и о работе с объектами страниц (обращения к ним делаются только через Page.get_object(L_I18N_TEXTFIELD_LOGIN)) можем по стеку вытащить имя локатора и сохранить его в Run-time Locators Storage.

Благодаря докстрингу метода локатора (“””поле ввода пароля”””) имеем элегантное решение где хранить человеческое описание локатора, которое и используется для инференса LLM.

Так выглядят сырые логи, демонстрирующие self-healing

Так выглядят сырые логи, демонстрирующие self-healing

Пока не делаем автоподмены старых локаторов на новые – сгенерированные LLM. Показываем их в allure-репорте.

Показываем Warning-locators self healed с указанием PageObject.LocatorName и нового LocatorValue

Показываем Warning-locators self healed с указанием PageObject.LocatorName и нового LocatorValue

Ограничения

  • Одновременное обращение в LLM за инференсом увеличивает TTFT для последующих запросов. Например, если сделать сразу 3 запроса на нашей первой конфигурации (TTFT=35), то ответ по первому получим через 35 секунд (ОК), по второму – через ~70 секунд и через ~105 секунд по третьему. Похоже на очередь.

  • Если решить использовать такой подход с конфигурацией, дающей долгий TTFT, для многих тестов/когда почти все локаторы отваливаются – всё встанет в очередь и ваш аналог Gitlab прекратит выполнения пайплайна с тестами по таймауту

  • В условиях ограниченных HW-ресурсов нельзя просто взять и подключить все локаторы к LLM – потенциально возникнет очередь из-за многопоточного запуска тестов, даже если ваш продукт стабилен и разработчики выделяют специальные свойства для автотестов (как e2e-id в нашем случае). Для минимизации этой проблемы есть простая стратегия: автоматически считаем сколько раз используется каждый локатор в тест ране и подключаем только самые часто используемые. Например, когда почти все ваши тесты начинаются со страницы авторизации, вы получите максимальное число использований локаторов для полей ввода логина, пароля и кнопки Войти. Подключив их вы предостережёте себя от провала всех тестов в самом начале.

  • Размер контекста – количество токенов в промпте. Чем больше размер – тем больше потребление памяти. Чем меньше размер – тем сильнее вам придётся ухищряться чтобы дать LLM модели подходящую часть HTML для inference. В нашем случае делаем предобработку – из всего page_source, отдаваемого Selenium’ом, вырезаем лишнее – теги <style></style> и <script></script> вместе с содержимым. Скорее всего будем урезать еще для страниц с большим количество элементов. Размер нашего контекста – 10000 токенов. Согласно документации qwen – 1 токен это в среднем 3-4 символа английского текста.

Перспективы

  1. Продукты, находящиеся в относительно активной разработке, теперь всё-таки могут иметь UI-автотесты благодаря self-healing подходу.

  2. Автотестирование относительно устоявшихся продуктов станет более надёжным за счет адаптируемости к изменениям в приложении.

  3. Автоматическое тестирование относительно устоявшихся продуктов может покрыть больше сценариев (например, в тестировании desktop платформ, где иногда требуется доступ к системным окнам) за счёт внедрения мультимодальных LLM либо комбинации нескольких простых моделей.

Автор: breakingtesting

Источник

Рейтинг@Mail.ru
Rambler's Top100