Динамически генерировать асинхронный тест драматурга, показывает: тест не найден

В документации Playwright есть такой пример параметризации. Оно работает:

const people = ['Alice', 'Bob'];
for (const name of people) {
  test(`testing with ${name}`, async () => {
    // ...
  });
  // You can also do it with test.describe() or with multiple tests as long the test name is unique.
}

Однако я хочу получать массив данных перед каждым тестом и генерировать тестовый цикл.

//data like data[] = [{name:'Alice',age:'20'},..]
let data: any[] = []; 

test.beforeEach(async ({ request }) => {
    
    data = await getData(request);
});

for (let i = 0; i < data.length; i++) {
  test(`testing with ${name}`, async (page) => {
    // ...
  });

}

В коде визуальной студии отображается ошибка: тест не найден.

Но это работает, когда я присваиваю точное значение let data: any[] =[{name:'Alice',age:'20'}];.

Я тестирую аналогичный случай https://github.com/microsoft/playwright/issues/9916Как правильно запустить отдельные тесты драматурга из динамического массива встроенных URL-адресов?

Есть ли в имени вашего тестового файла .spec. или .test.? Существует ли тестовый файл в месте, которое будет выбрано вашим файлом playwright.config?

AutomationAndy 14.03.2024 08:46

да, имя файла — xxx.spec.ts. Это работает, когда я присваиваю точное значение, пусть data: Any[] =[{name:'Alice',age:'20'}]

QQ321 14.03.2024 08:54
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
2
3
654
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

У меня плохое решение, которое определяет длину массива.

let data: any[] = new Array(10);

Затем тесты можно определить с помощью VScode

for (let i = 0; i < data.length; i++) {
  test(`testing with ${name}`, async page => {
    if (data[i] !== undefined) {
      // test
    }
    // ...
  });
}

Спасибо за ответ, приятно видеть обходной путь. Придирка: VSCode — это всего лишь редактор. Playwright — это библиотека, которая обнаруживает тесты.

ggorlen 14.03.2024 18:19

Это потому, что вы используете перехватчик beforeEach и, скорее всего, метод getData присваивает неопределенное значение data или data длина равна нулю.
Я рекомендую вам отладить то, что назначается data в хуке beforeEach.

Еще одна важная вещь: хук beforEach запускается один раз перед КАЖДЫМ тестом. В твоем случае,

  1. ваш тест начинается с пустого массива data;
  2. Ваш крючок сработает один раз;
  3. Но поскольку data пусто, тестовый запуск останавливается и процесс завершается с ошибкой: No Test found.

Решение:

import { test } from '@playwright/test';
import { getData } from '../utils/getData';

test.describe('Some feature', () => {
  let data: string[] = [];
  data = getData(); // Assuming it returns an array of strings

  data.forEach(testName => {
    test(`Test name: ${testName}`, () => {
      console.info('Logging test name:', testName);
    });
  });
});

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

Я не уверен, как вы реализовали приспособление request. Если это не сработает, импортируйте его в модуль и передайте непосредственно getData.

jkalandarov 14.03.2024 18:07

Мне это, к сожалению, не подходит. Я получаю ошибку, TypeError: Cannot read properties of undefined (reading 'length') относится к data.length. Хотя было бы круто/оптимально, если бы это сработало. Опечатка: beforAll.

ggorlen 14.03.2024 18:23

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

jkalandarov 14.03.2024 18:39

Это не связано с request, оно терпит неудачу, даже если data = ["a", "b", "c"], поэтому фундаментальная установка здесь, похоже, не работает независимо от getData. Пробовали ли вы запустить этот код самостоятельно, чтобы убедиться, что он работает? Проблема описана в моем ответе: обнаружение тестов — это двухэтапный процесс, поэтому на первом этапе не выполняются перехватчики, а асинхронный код практически запрещен.

ggorlen 14.03.2024 18:40

Я обновил решение. Это сработало для меня. Перед началом тестов вам следует убедиться, что data имеет длину.

jkalandarov 15.03.2024 07:43

Ваше обновление удаляет async/await, о чем и идет вопрос. Можете ли вы обновить, чтобы использовать это? В вашем ответе по-прежнему написано «Решение: используйте test.beforeAll», но этого больше нет в вашем коде.

ggorlen 15.03.2024 15:01

Давайте продолжим обсуждение в чате.

jkalandarov 15.03.2024 16:42
Ответ принят как подходящий

Фундаментальная проблема заключается в том, что Playwright запускает тестовый код дважды. Первый проход синхронный и предположительно используется для определения количества вызовов test(). Второй проход фактически выполняет тесты. Регистрация чего-либо на верхнем уровне тестового файла показывает этот двухэтапный процесс.

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

Это работает:

import {test} from "@playwright/test"; // ^1.42.1

(async () => {
  for (const name of ["a", "b", "c"]) {
    test(`testing with ${name}`, async () => {
      // ...
    });
  }
})();

но это не обнаруживает тесты:

import {setTimeout} from "node:timers/promises";
import {test} from "@playwright/test";

(async () => {
  await setTimeout(0); // actually do something async
  for (const name of ["a", "b", "c"]) {
    test(`testing with ${name}`, async () => {
      // ...
    });
  }
})();

Более того, хуки before не участвуют в процессе обнаружения тестов. Следующий код

import {test} from "@playwright/test";

console.info("TEST SUITE");
test.beforeAll(() => console.info("BEFORE"));

for (const name of ["a", "b", "c"]) {
  test(`testing with ${name}`, async () => {
    // ...
  });
}

выходы:

$ npx playwright test
TEST SUITE

Running 3 tests using 1 worker

TEST SUITE
  ✓  1 pw.test.js:7:7 › testing with a (5ms)
BEFORE
  ✓  2 pw.test.js:7:7 › testing with b (4ms)
  ✓  3 pw.test.js:7:7 › testing with c (2ms)

  3 passed (270ms)

Согласно этому комментарию, одним из обходных путей является использование test.step():

import {test} from "@playwright/test";

let people = [];

test.beforeAll(async ({request}) => {
  const url = "https://jsonplaceholder.typicode.com/users";
  const response = await request.get(url);
  people = (await response.json()).map(e => e.name);
});

test("test", async () => {
  for (const name of people) {
    await test.step(`testing with ${name}`, async () => {
      // ...
    });
  }
});

Проблема в том, что в выводе отсутствуют имена тестовых примеров:

$ npx playwright test

Running 1 test using 1 worker

  ✓  1 pw.test.js:11:5 › test (8ms)

  1 passed (407ms)

Другой обходной путь — сгенерировать данные в отдельном скрипте перед запуском тестов, сохранить их в файле и синхронно прочитать файл в начале тестов. Например:

генерировать-люди-data.js:

const fs = require("node:fs/promises");

(async () => {
  const url = "https://jsonplaceholder.typicode.com/users";
  const response = await fetch(url);
  const people = (await response.json()).map(e => e.name);
  await fs.writeFile("people.json", JSON.stringify(people));
})();

люди.test.js:

import fs from "node:fs";
import {test} from "@playwright/test";

const json = fs.readFileSync("people.json", {encoding: "utf-8"});
const people = JSON.parse(json);

for (const name of people) {
  test(`testing with ${name}`, async () => {
    // ...
  });
}

Бегать:

$ node generate-people-data && npx playwright test

Running 10 tests using 1 worker

  ✓  1 pw.test.js:7:7 › testing with Leanne Graham (9ms)
  ✓  2 pw.test.js:7:7 › testing with Ervin Howell (4ms)
  ✓  3 pw.test.js:7:7 › testing with Clementine Bauch (1ms)
  ✓  4 pw.test.js:7:7 › testing with Patricia Lebsack (1ms)
  ✓  5 pw.test.js:7:7 › testing with Chelsey Dietrich (1ms)
  ✓  6 pw.test.js:7:7 › testing with Mrs. Dennis Schulist (1ms)
  ✓  7 pw.test.js:7:7 › testing with Kurtis Weissnat (1ms)
  ✓  8 pw.test.js:7:7 › testing with Nicholas Runolfsdottir V (1ms)
  ✓  9 pw.test.js:7:7 › testing with Glenna Reichert (1ms)
  ✓  10 pw.test.js:7:7 › testing with Clementina DuBuque (3ms)

  10 passed (253ms)

Если вам не нравится &&, вы можете запустить npx playwright test на последнем этапе скрипта-генератора.

Другой вариант — метод синхронной выборки из другого потока.

Возможно, в текущей версии есть более элегантный подход, или будущие версии Playwright будут поддерживать обнаружение асинхронных тестов.

Другие вопросы по теме