Skip to main content

Утверждения

Введение

Playwright включает в себя тестовые утверждения в виде функции expect. Чтобы сделать утверждение, вызовите expect(value) и выберите сопоставитель, который отражает ожидание. Существует множество общих сопоставителей, таких как toEqual, toContain, toBeTruthy, которые можно использовать для утверждения любых условий.

expect(success).toBeTruthy();

Playwright также включает в себя веб-специфичные асинхронные сопоставители, которые будут ждать, пока ожидаемое условие не будет выполнено. Рассмотрим следующий пример:

await expect(page.getByTestId('status')).toHaveText('Submitted');

Playwright будет повторно тестировать элемент с тестовым идентификатором status, пока извлеченный элемент не будет содержать текст "Submitted". Он будет повторно извлекать элемент и проверять его снова и снова, пока условие не будет выполнено или пока не истечет время ожидания. Вы можете либо передать это время ожидания, либо настроить его один раз через значение testConfig.expect в конфигурации теста.

По умолчанию время ожидания для утверждений установлено на 5 секунд. Узнайте больше о различных таймаутах.

Автоматическое повторение утверждений

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

УтверждениеОписание
await expect(locator).toBeAttached()Элемент прикреплен
await expect(locator).toBeChecked()Флажок установлен
await expect(locator).toBeDisabled()Элемент отключен
await expect(locator).toBeEditable()Элемент редактируемый
await expect(locator).toBeEmpty()Контейнер пуст
await expect(locator).toBeEnabled()Элемент включен
await expect(locator).toBeFocused()Элемент в фокусе
await expect(locator).toBeHidden()Элемент не виден
await expect(locator).toBeInViewport()Элемент пересекает область просмотра
await expect(locator).toBeVisible()Элемент виден
await expect(locator).toContainText()Элемент содержит текст
await expect(locator).toHaveAccessibleDescription()Элемент имеет соответствующее доступное описание
await expect(locator).toHaveAccessibleName()Элемент имеет соответствующее доступное имя
await expect(locator).toHaveAttribute()Элемент имеет DOM-атрибут
await expect(locator).toHaveClass()Элемент имеет класс
await expect(locator).toHaveCount()Список имеет точное количество дочерних элементов
await expect(locator).toHaveCSS()Элемент имеет CSS-свойство
await expect(locator).toHaveId()Элемент имеет ID
await expect(locator).toHaveJSProperty()Элемент имеет JavaScript-свойство
await expect(locator).toHaveRole()Элемент имеет определенную ARIA роль
await expect(locator).toHaveScreenshot()Элемент имеет скриншот
await expect(locator).toHaveText()Элемент соответствует тексту
await expect(locator).toHaveValue()Поле ввода имеет значение
await expect(locator).toHaveValues()Выбор имеет выбранные опции
await expect(page).toHaveScreenshot()Страница имеет скриншот
await expect(page).toHaveTitle()Страница имеет заголовок
await expect(page).toHaveURL()Страница имеет URL
await expect(response).toBeOK()Ответ имеет статус OK

Утверждения без повторного выполнения

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

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

УтверждениеОписание
expect(value).toBe()Значение такое же
expect(value).toBeCloseTo()Число приблизительно равно
expect(value).toBeDefined()Значение не undefined
expect(value).toBeFalsy()Значение ложно, например, false, 0, null и т.д.
expect(value).toBeGreaterThan()Число больше
expect(value).toBeGreaterThanOrEqual()Число больше или равно
expect(value).toBeInstanceOf()Объект является экземпляром класса
expect(value).toBeLessThan()Число меньше
expect(value).toBeLessThanOrEqual()Число меньше или равно
expect(value).toBeNaN()Значение NaN
expect(value).toBeNull()Значение null
expect(value).toBeTruthy()Значение истинно, т.е. не false, 0, null и т.д.
expect(value).toBeUndefined()Значение undefined
expect(value).toContain()Строка содержит подстроку
expect(value).toContain()Массив или набор содержит элемент
expect(value).toContainEqual()Массив или набор содержит похожий элемент
expect(value).toEqual()Значение похоже - глубокое равенство и сопоставление шаблонов
expect(value).toHaveLength()Массив или строка имеют длину
expect(value).toHaveProperty()Объект имеет свойство
expect(value).toMatch()Строка соответствует регулярному выражению
expect(value).toMatchObject()Объект содержит указанные свойства
expect(value).toStrictEqual()Значение похоже, включая типы свойств
expect(value).toThrow()Функция выбрасывает ошибку
expect(value).any()Соответствует любому экземпляру класса/примитива
expect(value).anything()Соответствует чему угодно
expect(value).arrayContaining()Массив содержит определенные элементы
expect(value).closeTo()Число приблизительно равно
expect(value).objectContaining()Объект содержит определенные свойства
expect(value).stringContaining()Строка содержит подстроку
expect(value).stringMatching()Строка соответствует регулярному выражению

Отрицание сопоставителей

В общем случае, мы можем ожидать противоположное, добавив .not перед сопоставителями:

expect(value).not.toEqual(0);
await expect(locator).not.toContainText('some text');

Мягкие утверждения

По умолчанию, неудачное утверждение завершает выполнение теста. Playwright также поддерживает мягкие утверждения: неудачные мягкие утверждения не завершают выполнение теста, но помечают тест как неудачный.

// Сделайте несколько проверок, которые не остановят тест при неудаче...
await expect.soft(page.getByTestId('status')).toHaveText('Success');
await expect.soft(page.getByTestId('eta')).toHaveText('1 day');

// ... и продолжайте тест, чтобы проверить больше вещей.
await page.getByRole('link', { name: 'next page' }).click();
await expect.soft(page.getByRole('heading', { name: 'Make another order' })).toBeVisible();

В любой момент выполнения теста вы можете проверить, были ли какие-либо неудачи мягких утверждений:

// Сделайте несколько проверок, которые не остановят тест при неудаче...
await expect.soft(page.getByTestId('status')).toHaveText('Success');
await expect.soft(page.getByTestId('eta')).toHaveText('1 day');

// Избегайте дальнейшего выполнения, если были неудачи мягких утверждений.
expect(test.info().errors).toHaveLength(0);

Обратите внимание, что мягкие утверждения работают только с тестовым раннером Playwright.

Пользовательское сообщение expect

Вы можете указать пользовательское сообщение expect в качестве второго аргумента функции expect, например:

await expect(page.getByText('Name'), 'should be logged in').toBeVisible();

Это сообщение будет отображаться в отчетах, как для успешных, так и для неудачных expect, предоставляя больше контекста об утверждении.

Когда expect проходит, вы можете увидеть успешный шаг, подобный этому:

✅ should be logged in    @example.spec.ts:18

Когда expect не проходит, ошибка будет выглядеть так:

    Error: should be logged in

Call log:
- expect.toBeVisible with timeout 5000ms
- waiting for "getByText('Name')"


2 |
3 | test('example test', async({ page }) => {
> 4 | await expect(page.getByText('Name'), 'should be logged in').toBeVisible();
| ^
5 | });
6 |

Мягкие утверждения также поддерживают пользовательское сообщение:

expect.soft(value, 'my soft assertion').toBe(56);

expect.configure

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

const slowExpect = expect.configure({ timeout: 10000 });
await slowExpect(locator).toHaveText('Submit');

// Всегда выполняйте мягкие утверждения.
const softExpect = expect.configure({ soft: true });
await softExpect(locator).toHaveText('Submit');

expect.poll

Вы можете преобразовать любой синхронный expect в асинхронный с помощью expect.poll.

Следующий метод будет опрашивать данную функцию, пока она не вернет HTTP статус 200:

await expect.poll(async () => {
const response = await page.request.get('https://api.example.com');
return response.status();
}, {
// Пользовательское сообщение expect для отчетности, необязательно.
message: 'make sure API eventually succeeds',
// Опрос в течение 10 секунд; по умолчанию 5 секунд. Установите 0, чтобы отключить таймаут.
timeout: 10000,
}).toBe(200);

Вы также можете указать пользовательские интервалы опроса:

await expect.poll(async () => {
const response = await page.request.get('https://api.example.com');
return response.status();
}, {
// Пробовать, ждать 1с, пробовать, ждать 2с, пробовать, ждать 10с, пробовать, ждать 10с, пробовать
// ... По умолчанию [100, 250, 500, 1000].
intervals: [1_000, 2_000, 10_000],
timeout: 60_000
}).toBe(200);

expect.toPass

Вы можете повторять блоки кода, пока они не пройдут успешно.

await expect(async () => {
const response = await page.request.get('https://api.example.com');
expect(response.status()).toBe(200);
}).toPass();

Вы также можете указать пользовательский таймаут и интервалы повторов:

await expect(async () => {
const response = await page.request.get('https://api.example.com');
expect(response.status()).toBe(200);
}).toPass({
// Пробовать, ждать 1с, пробовать, ждать 2с, пробовать, ждать 10с, пробовать, ждать 10с, пробовать
// ... По умолчанию [100, 250, 500, 1000].
intervals: [1_000, 2_000, 10_000],
timeout: 60_000
});

Обратите внимание, что по умолчанию toPass имеет таймаут 0 и не учитывает пользовательский таймаут expect.

Добавление пользовательских сопоставителей с помощью expect.extend

Вы можете расширить утверждения Playwright, предоставив пользовательские сопоставители. Эти сопоставители будут доступны в объекте expect.

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

fixtures.ts
import { expect as baseExpect } from '@playwright/test';
import type { Page, Locator } from '@playwright/test';

export { test } from '@playwright/test';

export const expect = baseExpect.extend({
async toHaveAmount(locator: Locator, expected: number, options?: { timeout?: number }) {
const assertionName = 'toHaveAmount';
let pass: boolean;
let matcherResult: any;
try {
await baseExpect(locator).toHaveAttribute('data-amount', String(expected), options);
pass = true;
} catch (e: any) {
matcherResult = e.matcherResult;
pass = false;
}

const message = pass
? () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
'\n\n' +
`Locator: ${locator}\n` +
`Expected: not ${this.utils.printExpected(expected)}\n` +
(matcherResult ? `Received: ${this.utils.printReceived(matcherResult.actual)}` : '')
: () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
'\n\n' +
`Locator: ${locator}\n` +
`Expected: ${this.utils.printExpected(expected)}\n` +
(matcherResult ? `Received: ${this.utils.printReceived(matcherResult.actual)}` : '');

return {
message,
pass,
name: assertionName,
expected,
actual: matcherResult?.actual,
};
},
});

Теперь мы можем использовать toHaveAmount в тесте.

example.spec.ts
import { test, expect } from './fixtures';

test('amount', async () => {
await expect(page.locator('.cart')).toHaveAmount(4);
});

Совместимость с библиотекой expect

note

Не путайте expect Playwright с библиотекой expect. Последняя не полностью интегрирована с тестовым раннером Playwright, поэтому убедитесь, что вы используете собственный expect Playwright.

Комбинирование пользовательских сопоставителей из нескольких модулей

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

fixtures.ts
import { mergeTests, mergeExpects } from '@playwright/test';
import { test as dbTest, expect as dbExpect } from 'database-test-utils';
import { test as a11yTest, expect as a11yExpect } from 'a11y-test-utils';

export const expect = mergeExpects(dbExpect, a11yExpect);
export const test = mergeTests(dbTest, a11yTest);
test.spec.ts
import { test, expect } from './fixtures';

test('passes', async ({ database }) => {
await expect(database).toHaveDatabaseUser('admin');
});