| name | assertion-utilities |
| description | AssertUtils and ExpectUtils patterns for centralized test validation in Playwright. Use when implementing reusable assertions, soft assertions, or building a consistent validation layer across your test suite.
|
Assertion Utilities Skill
A comprehensive guide to implementing centralized assertion utilities (AssertUtils & ExpectUtils) in Playwright for consistent, reusable, and maintainable test validation.
Why Centralized Assertions?
As test suites grow, scattered and inconsistent assertions become a maintenance nightmare:
| Problem | Solution with Assertion Utilities |
|---|
| Inconsistent assertion messages | Standardized, descriptive error messages |
| Duplicate assertion logic | Reusable assertion methods |
| No soft assertion support | Built-in soft assertion mode |
| Hard to add custom validations | Extensible assertion classes |
| Mixed assertion styles | Unified API for all validations |
Architecture Overview
┌─────────────────────────────────────────────────────────┐
│ Test Files │
│ (use AssertUtils & ExpectUtils) │
└─────────────────────┬───────────────────────────────────┘
│
┌───────────┴───────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ AssertUtils │ │ ExpectUtils │
│ (Value-based) │ │ (DOM-based) │
│ │ │ │
│ • String equality │ │ • Element visible │
│ • Number comparison │ │ • Text content │
│ • Array contains │ │ • URL matching │
│ • Object matching │ │ • Attribute values │
│ • Type checking │ │ • Element state │
└─────────────────────┘ └─────────────────────┘
│ │
└───────────┬───────────┘
▼
┌─────────────────────┐
│ SoftAssertContext │
│ (Fail-later mode) │
└─────────────────────┘
Implementation
1. AssertUtils - Value-Based Assertions
export class AssertUtils {
private softMode: boolean = false;
private softErrors: Error[] = [];
enableSoftMode(): void {
this.softMode = true;
this.softErrors = [];
}
disableSoftModeAndValidate(): void {
this.softMode = false;
if (this.softErrors.length > 0) {
const messages = this.softErrors.map((e, i) => `${i + 1}. ${e.message}`).join('\n');
this.softErrors = [];
throw new ();
}
}
(): [] {
[....];
}
(: ): {
error = (message);
(.) {
..(error);
.();
} {
error;
}
}
assertEqual<T>(: T, : T, ?: ): {
(actual !== expected) {
.(
message ??
);
}
}
assertNotEqual<T>(: T, : T, ?: ): {
(actual === notExpected) {
.(
message ??
);
}
}
assertStrictEqual<T>(: T, : T, ?: ): {
(actual !== expected) {
.(
message ??
);
}
}
assertDeepEqual<T>(: T, : T, ?: ): {
actualStr = .(actual);
expectedStr = .(expected);
(actualStr !== expectedStr) {
.(
message ??
);
}
}
(: , ?: ): {
(actual !== ) {
.(message ?? );
}
}
(: , ?: ): {
(actual !== ) {
.(message ?? );
}
}
(: , ?: ): {
(actual !== ) {
.(message ?? );
}
}
(: , ?: ): {
(actual === ) {
.(message ?? );
}
}
(: , ?: ): {
(actual !== ) {
.(message ?? );
}
}
(: , ?: ): {
(actual === || actual === ) {
.(message ?? );
}
}
(: , : , ?: ): {
(actual <= expected) {
.(
message ??
);
}
}
(: , : , ?: ): {
(actual < expected) {
.(
message ??
);
}
}
(: , : , ?: ): {
(actual >= expected) {
.(
message ??
);
}
}
(: , : , ?: ): {
(actual > expected) {
.(
message ??
);
}
}
(: , : , : , ?: ): {
(actual < min || actual > max) {
.(
message ??
);
}
}
(: , : , : , ?: ): {
(.(actual - expected) > tolerance) {
.(
message ??
);
}
}
(: , : , ?: ): {
(!actual.(substring)) {
.(
message ??
);
}
}
(: , : , ?: ): {
(actual.(substring)) {
.(
message ??
);
}
}
(: , : , ?: ): {
(!actual.(prefix)) {
.(
message ??
);
}
}
(: , : , ?: ): {
(!actual.(suffix)) {
.(
message ??
);
}
}
(: , : , ?: ): {
(!pattern.(actual)) {
.(
message ??
);
}
}
(: , ?: ): {
(actual !== ) {
.(message ?? );
}
}
(: , ?: ): {
(actual === ) {
.(message ?? );
}
}
(: , : , ?: ): {
(actual.() !== expected.()) {
.(
message ??
);
}
}
assertArrayContains<T>(: T[], : T, ?: ): {
(!array.(element)) {
.(
message ??
);
}
}
assertArrayNotContains<T>(: T[], : T, ?: ): {
(array.(element)) {
.(
message ??
);
}
}
assertArrayLength<T>(: T[], : , ?: ): {
(array. !== expectedLength) {
.(
message ??
);
}
}
assertArrayEmpty<T>(: T[], ?: ): {
(array. !== ) {
.(
message ??
);
}
}
assertArrayNotEmpty<T>(: T[], ?: ): {
(array. === ) {
.(message ?? );
}
}
assertArraysEqual<T>(: T[], : T[], ?: ): {
sortedActual = [...actual].();
sortedExpected = [...expected].();
(.(sortedActual) !== .(sortedExpected)) {
.(
message ??
);
}
}
(: , : , ?: ): {
( actual !== expectedType) {
.(
message ??
);
}
}
assertInstanceOf<T>(: , : (...: []) => T, ?: ): {
(!(actual expectedClass)) {
.(
message ??
);
}
}
}
2. ExpectUtils - DOM-Based Assertions
import { Page, Locator, expect } from '@playwright/test';
export class ExpectUtils {
private page: Page;
private softMode: boolean = false;
private softErrors: Error[] = [];
constructor(page: Page) {
this.page = page;
}
enableSoftMode(): void {
this.softMode = true;
this.softErrors = [];
}
disableSoftModeAndValidate(): void {
this.softMode = false;
if (this.softErrors.length > 0) {
const messages = this..( ).();
. = [];
();
}
}
(): [] {
[....];
}
(: <>): <> {
(.) {
{
();
} (error) {
..(error );
.();
}
} {
();
}
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , ?: ): <> {
.( () => {
(locator, message).({ : });
});
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , ?: ): <> {
.( () => {
(locator, message).({ : });
});
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , : | , ?: ): <> {
.( () => {
(locator, message).(text);
});
}
(: , : | , ?: ): <> {
.( () => {
(locator, message).(text);
});
}
(: , ?: ): <> {
.( () => {
(locator, message).();
});
}
(: , : | , ?: ): <> {
.( () => {
(locator, message).(value);
});
}
(: , : [], ?: ): <> {
.( () => {
(locator, message).(values);
});
}
(
: ,
: ,
?: | ,
?:
): <> {
.( () => {
(value !== ) {
(locator, message).(name, value);
} {
(locator, message).(name);
}
});
}
(: , : | , ?: ): <> {
.( () => {
(locator, message).(className);
});
}
(
: ,
: ,
: | ,
?:
): <> {
.( () => {
(locator, message).(name, value);
});
}
(: , : | , ?: ): <> {
.( () => {
(locator, message).(id);
});
}
(: , : , ?: ): <> {
.( () => {
(locator, message).(count);
});
}
(: | , ?: ): <> {
.( () => {
(., message).(title);
});
}
(: | , ?: ): <> {
.( () => {
(., message).(url);
});
}
(: , : , ?: ): <> {
.( () => {
(locator).(name, options);
});
}
(: , ?: ): <> {
.( () => {
(.).(name, options);
});
}
}
3. Combined Assertions Class
import { Page } from '@playwright/test';
import { AssertUtils } from './AssertUtils';
import { ExpectUtils } from './ExpectUtils';
export class Assertions {
private _assert: AssertUtils;
private _expect: ExpectUtils;
constructor(page: Page) {
this._assert = new AssertUtils();
this._expect = new ExpectUtils(page);
}
assert(): AssertUtils {
return this._assert;
}
expect(): ExpectUtils {
return this._expect;
}
(): {
..();
..();
}
(): {
assertErrors = ..();
expectErrors = ..();
..();
..();
allErrors = [...assertErrors, ...expectErrors];
(allErrors. > ) {
messages = allErrors.( ).();
();
}
}
}
Using Assertions with Fixtures
import { test as base } from '@playwright/test';
import { Assertions } from '../assertions/Assertions';
type AssertionFixtures = {
assertions: Assertions;
};
export const test = base.extend<AssertionFixtures>({
assertions: async ({ page }, use) => {
const assertions = new Assertions(page);
await use(assertions);
},
});
export { expect } from '@playwright/test';
Combined Fixture (UIActions + Assertions)
import { test as base } from '@playwright/test';
import { UIActions } from '../actions/UIActions';
import { Assertions } from '../assertions/Assertions';
type CombinedFixtures = {
ui: UIActions;
assertions: Assertions;
};
export const test = base.extend<CombinedFixtures>({
ui: async ({ page }, use) => {
await use(new UIActions(page));
},
assertions: async ({ page }, use) => {
await use(new Assertions(page));
},
});
export { expect } from '@playwright/test';
Usage Examples
Basic Usage
import { test } from '../fixtures/combined.fixture';
import { LoginPage } from '../pages/LoginPage';
test('user can login', async ({ page, ui, assertions }) => {
const loginPage = new LoginPage(page, ui);
await ui.pageAction().navigate('/login');
await loginPage.login('user@example.com', 'password');
await assertions.expect().toHaveURL(/.*dashboard/);
await assertions.expect().toBeVisible(page.getByText('Welcome'));
const pageTitle = await ui.pageAction().getTitle();
assertions.assert().assertEqual(pageTitle, 'Dashboard');
});
Using Soft Assertions
test('validate form fields', async ({ page, assertions }) => {
assertions.enableSoftMode();
const nameInput = page.getByLabel('Name');
const emailInput = page.getByLabel('Email');
const phoneInput = page.getByLabel('Phone');
await assertions.expect().toBeVisible(nameInput, 'Name field should be visible');
await assertions.expect().toBeVisible(emailInput, 'Email field should be visible');
await assertions.expect().toBeVisible(phoneInput, 'Phone field should be visible');
await assertions.expect().toHaveValue(nameInput, '', 'Name should be empty initially');
await assertions.expect().toHaveValue(emailInput, '', 'Email should be empty initially');
assertions.disableSoftModeAndValidate();
});
In Page Objects
import { Page, Locator } from '@playwright/test';
import { UIActions } from '../actions/UIActions';
import { Assertions } from '../assertions/Assertions';
export class ProductPage {
readonly productTitle: Locator;
readonly productPrice: Locator;
readonly addToCartButton: Locator;
constructor(
private page: Page,
private ui: UIActions,
private assertions: Assertions
) {
this.productTitle = page.getByRole('heading', { level: 1 });
this.productPrice = page.getByTestId('product-price');
this.addToCartButton = page.getByRole('button', { name: });
}
(: , : ): <> {
..().(., expectedName);
priceText = ..().(.);
price = (priceText.(, ));
..().(price, expectedPrice, );
}
(): <> {
..().(.);
..().(
..(),
);
}
}
Folder Structure
your-project/
├── assertions/
│ ├── AssertUtils.ts # Value-based assertions
│ ├── ExpectUtils.ts # DOM-based assertions
│ ├── Assertions.ts # Combined gateway
│ └── index.ts # Barrel exports
├── actions/
│ ├── UIActions.ts
│ └── ...
├── pages/
│ └── ...
├── fixtures/
│ ├── ui.fixture.ts
│ ├── assertions.fixture.ts
│ └── combined.fixture.ts
└── tests/
└── ...
Best Practices
Do's
Don'ts
Custom Assertion Example
import { Locator } from '@playwright/test';
import { ExpectUtils } from './ExpectUtils';
export class CustomAssertions extends ExpectUtils {
async toBeValidProductCard(cardLocator: Locator): Promise<void> {
await this.toBeVisible(cardLocator.getByRole('img'), 'Product image should be visible');
await this.toBeVisible(cardLocator.getByRole('heading'), 'Product title should be visible');
await this.toBeVisible(cardLocator.getByTestId('price'), 'Product price should be visible');
await this.toBeEnabled(cardLocator.getByRole('button', { name: 'Add to Cart' }));
}
async (: , : , : ): <> {
errorLocator = formLocator.().({ : errorMessage });
.(errorLocator, );
}
}
Related Resources