ОБНОВЛЕНИЕ: скоро весь этот пост (https://github.com/facebook/jest/pull/5601) больше не нужен, и то же самое можно сделать, используя: jest --coverage --changedSince=master
Если вы используете Jest сегодня и запускаете весь свой пакет тестирования при каждом PR (запрос на вытягивание) для создания отчетов о покрытии кода, прекратите то, что вы делаете. Потратьте 5 минут на чтение этого сообщения, это будет лучшим вложением, которое вы сделали за все лето!
На первый взгляд совет использовать отчеты о покрытии для запросов на вытягивание и тестировать только измененные файлы может показаться несовместимым друг с другом.
Au contraire! То, как происходит волшебство, держится в секрете.
Если вы хотите обеспечить определенный порог охвата для каждого PR, без необходимости запускать весь комплект тестирования, Jest поможет нам.
Этот пост проведет вас через следующие шаги:
- Как перечислить все изменения между двумя ветками git, относящиеся к Jest.
- Использование Jest API для поиска всех связанных тестов.
- Попросите Jest исключить неизмененные файлы из отчета о покрытии кода.
Что изменилось
Давайте использовать execa, чтобы мы могли легко связать наши команды cli.
Сначала мы просим git сообщить нам, какие файлы изменились между базовой веткой (remotes / origin / develop) и нашей собственной веткой (на travis имя TRAVIS_BRANCH, как вы поняли).
Затем с помощью команды grep мы получаем только файлы .js, чтобы отфильтровать непроверенные файлы.
const execa = require("execa"); const branch = process.env.CIRCLE_BRANCH; execa .shell( `git diff --name-only remotes/origin/master...${branch} | grep .js$` ) .then(({ stdout, stderr }) => { if (stderr) { throw new Error(stderr); } return stdout.replace(/\n/g, " "); });
Найдите все связанные тесты
—-findRelatedTests
- одна из лучших функций Jest. В сочетании с --listTests
и 🎉 у вас есть полный список всех тестов, связанных с вашими измененными файлами.
Добавляем этот фрагмент кода к предыдущему:
.then(changedFiles => execa .shell( `jest --listTests --findRelatedTests ${changedFiles}` ) .then(({ stdout, stderr }) => { if (stderr) { throw new Error(stderr); } return { changedFiles: changedFiles.split(" "), relatedTests: JSON.parse(stdout) }; }) );
changedFiles
будет использоваться, чтобы сообщить Jest, что мы хотим проверить на предмет покрытия тестами. relatedTests
будет использоваться, чтобы решить, какие тесты запускать.
Запуск тестов
Используя --collectCoverageFrom
, мы переключаем Jest с проверки всех js-файлов в вашем проекте на только измененные.
В конце мы просто указываем, какие тесты запускать.
.then(ctx => { if (ctx.relatedTests.length < 1) { // wow, nothing to test! return; } const collectCoverageFrom = ctx.changedFiles .map(from => `--collectCoverageFrom "${from}"`) .join(" "); const testFiles = ctx.relatedTests .map(testFile => path.relative(process.cwd(), testFile)) .join(" "); const coverageCommand = `jest --coverage ${collectCoverageFrom} ${testFiles}`; return execa .shell(coverageCommand, { stdio: "inherit" }) .catch(() => { process.exitCode = 1; }); });
Вот и все 😄.
Полный пример
const execa = require("execa"); const path = require("path"); const branch = process.env.CIRCLE_BRANCH; execa .shell( `git diff --name-only remotes/origin/master...${branch} | grep .js$` ) .then(({ stdout, stderr }) => { if (stderr) { throw new Error(stderr); } return stdout.replace(/\n/g, " "); }) .then(changedFiles => execa .shell( `${jest} --listTests --findRelatedTests ${changedFiles}` ) .then(({ stdout, stderr }) => { if (stderr) { throw new Error(stderr); } return { changedFiles: changedFiles.split(" "), relatedTests: JSON.parse(stdout) }; }) ) .then(ctx => { if (ctx.relatedTests.length < 1) { // wow, nothing to test! return; } const collectCoverageFrom = ctx.changedFiles .map(from => `--collectCoverageFrom "${from}"`) .join(" "); const testFiles = ctx.relatedTests .map(testFile => path.relative(process.cwd(), testFile) ) .join(" "); const coverageCommand = `jest --coverage ${collectCoverageFrom} ${testFiles}`; return execa .shell(coverageCommand, { stdio: "inherit" }) .catch(() => { process.exitCode = 1; }); });