Canyon с открытым исходным кодом: комплексный инструмент анализа покрытия для улучшения качества кода JavaScript
Canyon с открытым исходным кодом: комплексный инструмент анализа покрытия для улучшения качества кода JavaScript

Об авторе

wr_zhang25, старший инженер по интерфейсной разработке в Ctrip, специализируется на покрытии внешнего кода и направлении открытого исходного кода JavaScript.

Лян, старший менеджер по исследованиям и разработкам Ctrip и эксперт по качеству, специализируется на области разработки качества.

1. Предыстория

istanbuljs Это отличный инструмент покрытия кода JavaScript, который в основном используется для обнаружения покрытия кода модульного тестирования и создания локальных отчетов о покрытии. Однако с развитием современных интерфейсных технологий и автоматизированного тестирования пользовательского интерфейса потребность в обнаружении покрытия кода для сквозного тестирования постепенно увеличивается, а функции, предоставляемые istanbuljs, кажутся расширенными.

JavaScript внутри покрытия Ctrip код использует встроенную отчетность о покрытии gitlab,Он также поддерживает только сбор покрытия модульных тестов и отображение обзорных данных. Поскольку интерфейсная технология Ctrip становится все более сложной,,У нас есть собственная платформа для регистрации трафика.,И развернули довольно масштабный кластер симуляторов дляUIавтоматизация(flybirds)Воспроизведение。В этом сценарии,Требуется сквозное тестированиепокрытие код собирайте и отображайте, чтобы студенты-разработчики могли лучше понять свои собственные кода。

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

В настоящее время несколько отделов Ctrip начали использовать Canyon, вставлять тестовый код на этапе построения конвейера непрерывной интеграции, а также собирать и сообщать данные о покрытии на этапе автоматического тестирования пользовательского интерфейса. Сервер генерирует подробные отчеты о покрытии в режиме реального времени, предоставляя полные индикаторы данных о покрытии для тестовых сценариев автоматизации пользовательского интерфейса.

2. Введение

Canyon может обеспечить инструментирование кода, создание отчетов о покрытии и создание отчетов в реальном времени с помощью простой настройки подключаемого модуля Babel. Его технологический стек полностью основан на JavaScript и для запуска требуется только среда Node.js. Он прост в развертывании и подходит для развертывания в облачных средах (таких как Docker и Kubernetes).

Архитектурный дизайн приложения подходит для обработки высокочастотных и крупномасштабных отчетов по данным покрытия, а также может обрабатывать различные сценарии автоматического тестирования пользовательского интерфейса. В то же время Canyon легко интегрируется с существующими инструментами CI/CD (такими как GitLab CI, Jenkins), что позволяет пользователям легко использовать его в конвейерах непрерывной интеграции.

Схема архитектуры выглядит следующим образом:

Основные функции Canyon будут представлены в следующих частях:

  • покрытие кода
  • Инструментирование кода
  • Тестирование и отчетность
  • агрегирование покрытия
  • отчет о покрытии
  • изменятьпокрытие кода
  • План сбора собственного покрытия React
  • Список приоритетов улучшения покрытия

3. Покрытие кода

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

Этап внедрения зондов кода в код называется инструментированием. Код перед инструментированием:

Язык кода:javascript
копировать
// add.js
function add(a, b) {
  return a + b
}
module.exports = { add }

Инструментирование — это процесс анализа кода для поиска всех функций, операторов и ветвей, а затем вставки счетчиков в код. Для приведенного выше кода после завершения инструментирования:

Язык кода:javascript
копировать
// Этот объект используется для подсчета количества выполнения каждой функции и каждого оператора.
const c = (window.__coverage__ = {
  // "f" Запишите количество вызовов каждой функции.
  f: [0],
  // "s" Запишите, сколько раз вызывается каждый оператор.
  // У нас есть 3 утверждения, и все они начинаются с 0.
  s: [0, 0, 0],
})

// Первый оператор определяет функцию
c.s[0]++
function add(a, b) {
  // После вызова функции второй оператор
  c.f[0]++
  c.s[1]++

  return a + b
}
// Третье утверждение вот-вот будет называться
c.s[2]++
module.exports = { add }

Мы хотим убедиться, что каждый оператор и функция в файле add.js были выполнены нашими тестами хотя бы один раз. Итак, пишем тест:

Язык кода:javascript
копировать
// add.cy.js
const { add } = require('./add')

it('adds numbers', () => {
  expect(add(2, 3)).to.equal(5)
})

Когда тест вызывает add(2, 3), счетчик внутри функции add увеличивается, и объект покрытия становится:

Язык кода:javascript
копировать
{
  f: [1],
  s: [1, 1, 1]
}

Покрытие этого тестового примера достигает 100%, и каждая функция и каждый оператор выполняется хотя бы один раз. Но в реальных приложениях для достижения 100% покрытия кода требуется несколько тестов.

Это базовое введение в освещение. Благодаря этим необходимым знаниям каждому будет легче понять следующее содержание.

4. Инструментирование кода (инструментирование-код)

Наиболее важной частью покрытия кода является инструментирование кода.

istanbuljs — это проверенный в боях золотой стандарт инструментирования кода JS. Canyon в основном предоставляет решения для сквозного тестирования. После большого количества экспериментов было проверено, что инструменты покрытия современного интерфейсного проектирования должны компилироваться во время компиляции. Конкретная причина заключается в том, что инструментарий nyc, предоставляемый istanbuljs, может инструментировать только собственный js. Однако существует бесконечное количество синтаксисов шаблонов внешнего интерфейса, таких как ts, tsx и vue. Хотя nyc также можно инструментировать, структурная практика доказала это. Эффект покрытия от прямого инструментирования Неудовлетворительный, невозможно точно определить функции, операторы и ветви, которые следует инструментировать.

К счастью, после исследования,мы узналиbabel-plugin-istanbul、vite-plugin-istanbul(experimental)、swc-плагин-покрытие-инструмент (экспериментальный). Свайные решения для других типов проектов. Все без исключения эти методы представляют собой инструментированные вызовы методов в соответствующий момент на этапе компиляции интерфейса, когда код анализируется в абстрактном синтаксическом дереве ast, и инструментированные функции более точно、заявление、ветвь.

Применимые типы проектов:

Тип проекта

план

vanilla javascript

nyc

babel

babel-plugin-istanbul

vite

vite-plugin-istanbul (experimental)

swc

swc-plugin-coverage-instrument (experimental)

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

Возьмем в качестве примера Babel.config.js:

Язык кода:javascript
копировать
module.exports = {
  plugins: [
    [
      'babel-plugin-istanbul',
      {
        exclude: ['**/*.spec.js', '**/*.spec.ts', '**/*.spec.tsx', '**/*.spec.jsx'],
      },
    ],
  ],
};

После завершения инструментирования в код будут вставлены некоторые зонды кода. Эти зонды кода будут собирать данные о покрытии во время выполнения, а затем передавать их на сервер Canyon.

Чтобы проверить успешность инструментирования, вы можете выполнить поиск по запросу __coverage__ в скомпилированном продукте. Если он существует, значит, инструментирование прошло успешно.

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

Мы также предоставляем плагин Babel Babel-plugin-canyon, который может считывать переменные среды (branch, sha) в различных конвейерах (aws, gitlab ci), чтобы последующие данные о покрытии можно было связать с соответствующим исходным кодом gitlab.

babel.config.js

Язык кода:javascript
копировать
module.exports = {
  plugins: [
    [
        'babel-plugin-canyon',
        {
          provider: 'gitlab',
          branch: process.env.CI_COMMIT_REF_NAME,
          sha: process.env.CI_COMMIT_SHA,
        },
    ],
  ],
};

Поддерживаемые провайдеры:

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

5. Тестирование и отчетность

Когда инструментарий будет завершен и выпущен в тестовую среду, мы сможем его протестировать. Возьмем, к примеру, драматург. Для успешно оснащенного сайта клиентского приложения к объекту окна будут подключены объекты __coverage__ и __canyon__. Нам необходимо собирать и передавать эти данные на сервер canyon во время процесса тестирования драматурга.

пример драматурга:

Язык кода:javascript
копировать
const {chromium} = require('playwright');
const main = async () => {
  const browser = await chromium.launch()
  const page = await browser.newPage();
  // Введите тестируемую страницу
  await page.goto('http://test.com')
  // Выполнение тестовых случаев
  // Вариант использования 1
  await page.click('button')
  // Вариант использования 2
  await page.fill('input', 'test')
  // Вариант использования 3
  await page.click('text=submit')
  const coverage = await page.evaluate(`window.__coverage__`)
  // Соберите отчетность
  upload(coverage)
  browser.close()
}

main()

У Ctrip есть собственная платформа автоматизации пользовательского интерфейса Flybirds, и мы интегрировали сбор и отчетность данных о покрытии Canyon в Flybirds. Сценарий сбора покрытия при реальном автоматизированном тестировании пользовательского интерфейса браузера относительно сложен, что в основном отражается в неопределенности времени сбора покрытия для многостраничных тестов (MPA).

Одностраничный (SPA) и многостраничный (MPA)

После выполнения тестового примера для одностраничных приложений (SPA) или многостраничных приложений этап создания отчета заключается в сообщении объекта __coverage__ в объекте окна страницы на сервер Canyon. Для одностраничных приложений, условно говоря, это так. относительно просто. Когда весь тестовый контент находится в одностраничном приложении, данные о покрытии будут постоянно находиться в объекте окна. Для многостраничных приложений переходы при маршрутизации приведут к копированию объекта окна и потере объекта. Таким образом, этот момент имеет решающее значение. После долгих практических проверок мы нашли метод onvisiblechange браузера.

  • visibilitychange

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

  • fetchLater

Chrome активно внедряет революционный API JavaScript — fetchLater(). Этот новый API призван полностью упростить процесс отправки данных при закрытии страницы, гарантируя, что даже после закрытия страницы или ухода пользователя запрос может быть выполнен безопасно и надежно в какой-то момент в будущем.

Запуск этого API является захватывающим. Он может решить сложную проблему многостраничного сбора данных (MPA), и его необходимо собирать только при закрытии браузера.

Примечание. fetchLater() уже доступен в Chrome для тестирования на реальных пользователях в оригинальной пробной версии, начиная с версии 121 (выпущенной в январе 2024 г.) и продолжаясь до Chrome 126 (июль 2024 г.).

6. Агрегация

Источником данных о покрытии является та же версия кода. Данные о покрытии могут быть агрегированы внутри компании Canyon, используя reportID для связывания тестовых примеров и измерений агрегирования сегментов. Это позволяет агрегировать массивные данные о охвате в ограниченное число, то есть количество случаев.

Язык кода:javascript
копировать
/**
 * Объединяет экземпляры объекта покрытия файлов двух идентичных файлов, чтобы обеспечить правильность счетчика выполнения.
 *
 * @method mergeFileCoverage
 * @static
 * @param {Object} first Первый объект переопределения файла для данного файла.
 * @param {Object} second Второй файл перезаписывает объект того же файла
 * @return {Object} Объединенный объект результата. Обратите внимание, что входные объекты не изменяются.
 */
function mergeFileCoverage(first, second) {
  const ret = JSON.parse(JSON.stringify(first));

  delete ret.l; // Удалить производную информацию

  Object.keys(second.s).forEach(function (k) {
    ret.s[k] += second.s[k];
  });

  Object.keys(second.f).forEach(function (k) {
    ret.f[k] += second.f[k];
  });

  Object.keys(second.b).forEach(function (k) {
    const retArray = ret.b[k];
    const secondArray = second.b[k];
    for (let i = 0; i < retArray.length; i += 1) {
      retArray[i] += secondArray[i];
    }
  });

  return ret;
}

Одной из характеристик данных о покрытии сквозным тестированием является большой размер отдельного объема данных, который эквивалентен 30 % общего объема исходного кода при инструментировании всего проекта. Количество отчетов по автоматизации пользовательского интерфейса на странице бронирования сайта рейсов Ctrip Trip.com может достигать 2000 раз каждый раз, каждый раз с 10 млн данных. Такой объем данных является огромной проблемой для сервера Canyon.

Для сценариев отчетности о данных, где один фрагмент данных большой и частый, трудно выполнить вычисления агрегирования данных в реальном времени. Canyon использует данные в виде очередей сообщений и спроектирован как служба без отслеживания состояния. Он подходит для контейнерного развертывания в эпоху облачных вычислений. Он может использовать эластичное масштабирование HPA для применения отчетов о тестовом покрытии в различных сценариях.

7. Отчет

Для отображения отчета о покрытии мы использовали стиль интерфейса istanbul-report. Однако, поскольку istanbul-report обеспечивает генерацию только статических html-файлов, по этой причине он не подходит для современного внешнего режима генерации данных гидратации. , мы ссылались на его исходный код, чтобы отметить покрытие исходного кода.

Язык кода:javascript
копировать
 const decorations = useMemo(() => {
    if (data) {
        const annotateFunctionsList = annotateFunctions(data.coverage, data.sourcecode);
        const annotateStatementsList = annotateStatements(data.coverage);
        return [...annotateStatementsList, ...annotateFunctionsList].map((i) => {
            return {
                inlineClassName: 'content-class-found',
                startLine: i.startLine,
                startCol: i.startCol,
                endLine: i.endLine,
                endCol: i.endCol,
            };
        });
    } else {
        return [];
    }
}, [data]);

Эффект после окрашивания:

8. Изменение покрытия кода

Для измененного покрытия кода мы рассчитываем формулу: новые покрытые строки кода/все новые строки кода.

Укажите цель сравнения, настроив CompareTarget, а затем объедините ее с интерфейсом git diff gitlab, чтобы получить измененные строки кода и вычислить данные покрытия.

Язык кода:javascript
копировать
/**
 * returns computed line coverage from statement coverage.
 * This is a map of hits keyed by line number in the source.
 */
function getLineCoverage(statementMap:{ [key: string]: Range },s:{ [key: string]: number }) {
  const statements = s;
  const lineMap = Object.create(null);

  Object.entries(statements).forEach(([st, count]) => {
    if (!statementMap[st]) {
      return;
    }
    const { line } = statementMap[st].start;
    const prevVal = lineMap[line];
    if (prevVal === undefined || prevVal < count) {
      lineMap[line] = count;
    }
  });
  return lineMap;
}

9. реагировать native сбор покрытияплан

Стек мобильных технологий Ctrip в основном основан на реагировании Родной, хорошая новость в том, что он применим и к нашему плану приборостроения, ведь все они собраны на базе Babel. И это эффективно для реагирования внутри компании. Собственная структура проекта унифицирована. Мы реализуем инструменты времени компиляции в конвейере и упаковываем «обычный пакет» и «пакет инструментов» соответственно в конвейер. Таким образом, в сочетании с автоматизацией пользовательского интерфейса, создается полный набор записи и воспроизведения. может быть сформирована система сбора индикаторов покрытия.

Используйте веб-сокет, чтобы предоставить данные о покрытии в симуляторе:

Язык кода:javascript
копировать
// Создать соединение WebSocket
const socket = new WebSocket('ws://localhost:8080');

// Запускается при открытии соединения WebSocket
socket.onopen = () => {
    console.log('Connected to coverage WebSocket server');
};

// Срабатывает при получении сообщения WebSocket
socket.onmessage = event => {
    try {
      if (JSON.parse(event.data).type === 'getcoverage') {
        // Отправить данные о покрытии
        socket.send(JSON.stringify(payload));
      }
    } catch (e) {
      console.log(e);
    }
};

// Запускается, когда соединение WebSocket закрывается
socket.onclose = () => {
    console.log('Disconnected from coverage WebSocket server');
};

В настоящее время модули APP отдела авиабилетов Ctrip подключены к Canyon. После практики Стамбул может очень хорошо выполнять сбор данных о покрытии и инструментах. Команда тестировщиков будет измерять это с помощью показателей покрытия Canyon перед каждым выпуском продукции. этот выпуск.

10. Список приоритетов улучшения покрытия

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

Чтобы решить эту проблему, мы провели углубленное исследование и обнаружили, что в компании уже есть развитая система сбора данных о покрытии кода производственной среды. Основываясь на этом выводе, мы решили объединить данные из этой системы с нашими собственными данными о покрытии, чтобы создать «список приоритетов улучшения покрытия». Цель этого списка — предоставить командам разработчиков четкие рекомендации о том, где следует уделять приоритетное внимание улучшению покрытия кода.

Чтобы сделать это руководство более научным и практичным, мы сформулировали формулу веса покрытия:

Охват производственной среды × 100 × 0,3 + (1 – тестовое покрытие) × 100 × 0,3 + количество функций × 0,2

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

11. Продвижение сообщества

На момент публикации этой статьи мы официально откроем исходный код Canyon.JavaScriptСегодня это самый популярный язык программирования.,Однако поле сбора сквозного тестового покрытия осталось пустым.,Наша разработка кода основана на istanbuljs,monaco Редактор и другие отличные проекты с открытым исходным кодом, мы уверены, что запуск Canyon с открытым исходным кодом может вызвать отклик сообщества, и в нем сможет принять участие большое количество разработчиков JavaScript.

У Canyon еще есть много возможностей для развития в будущем. Например, набор инструментов производственной среды еще не проверен и не опробован, и нет глубокой связи с инструментами автоматического тестирования, такими как драматург, кукольник и кипарис. было запланировано в дальнейших планах развития. Я надеюсь, что больше людей в Ctrip и сообществе смогут принять участие в строительстве Каньона в будущем.

Справочная ссылка

Проект с открытым исходным кодом Canyon:

https://github.com/canyon-project/canyon

Инструменты покрытия JavaScript:

https://github.com/istanbuljs/istanbuljs

Редактор кода на основе браузера:

https://github.com/microsoft/monaco-editor

Различия в тексте JavaScript:

https://github.com/kpdecker/jsdiff

"An O(ND) Difference Algorithm and its Variations" (Myers, 1986).

http://www.xmailserver.org/diff2.pdf

Делитесь, общайтесь, развивайтесь

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose