Зачем пользовательские проверки?

Встроенные проверки generic. assertEquals(expected, actual) работает для любого сравнения, но сообщение о падении лишено контекста. Пользовательские проверки добавляют доменный контекст: assertThat(user).isActive() выдаёт: Expected user "alice@example.com" to be active, but status was SUSPENDED.

Построение в Java

AssertJ Custom Assertions

public class UserAssert extends AbstractAssert<UserAssert, User> {
    public UserAssert(User actual) { super(actual, UserAssert.class); }

    public static UserAssert assertThat(User user) { return new UserAssert(user); }

    public UserAssert isActive() {
        isNotNull();
        if (!actual.isActive()) {
            failWithMessage("Expected user <%s> to be active but status was <%s>",
                actual.getEmail(), actual.getStatus());
        }
        return this;
    }

    public UserAssert hasRole(String role) {
        isNotNull();
        if (!actual.getRole().equals(role)) {
            failWithMessage("Expected user <%s> to have role <%s> but had <%s>",
                actual.getEmail(), role, actual.getRole());
        }
        return this;
    }
}

// Читается как спецификация
UserAssert.assertThat(user).isActive().hasRole("admin");

Пользовательские проверки в JavaScript

Playwright Custom Matchers

expect.extend({
    async toBeLoggedIn(page) {
        const isLoggedIn = await page.locator('.user-menu').isVisible();
        return {
            pass: isLoggedIn,
            message: () => isLoggedIn
                ? 'Не ожидалось, что страница будет залогинена'
                : 'Ожидалось, что страница залогинена, но меню пользователя не найдено'
        };
    },
});

await expect(page).toBeLoggedIn();

Мягкие проверки (Soft Assertions)

Стандартные проверки останавливают тест при первом падении. Мягкие собирают все падения и отчитываются в конце.

Java (AssertJ SoftAssertions)

@Test
void shouldValidateUserProfile() {
    SoftAssertions softly = new SoftAssertions();
    softly.assertThat(user.getName()).isEqualTo("Alice");
    softly.assertThat(user.getEmail()).contains("@");
    softly.assertThat(user.getRole()).isEqualTo("admin");
    softly.assertThat(user.isActive()).isTrue();
    softly.assertAll(); // Отчитывается обо ВСЕХ падениях
}

Playwright Soft Assertions

test('валидация элементов дашборда', async ({ page }) => {
    await page.goto('/dashboard');
    await expect.soft(page.locator('.welcome')).toHaveText('Welcome, Admin');
    await expect.soft(page.locator('.stats')).toBeVisible();
    await expect.soft(page.locator('.recent-orders')).toHaveCount(5);
});

Когда что использовать

ТипКогдаПример
ВстроенныеПростые сравненияassertEquals(200, statusCode)
ПользовательскиеДоменная валидация с понятными сообщениямиassertThat(user).isActive()
МягкиеВалидация нескольких независимых свойствПроверка всех полей профиля
HamcrestСложные композиции условийeveryItem(hasProperty("active", is(true)))

Упражнения

Упражнение 1: Доменные проверки

Создайте проверки для Order, напишите тесты, сравните сообщения с встроенными.

Упражнение 2: Сюита мягких проверок

Напишите тест, валидирующий 8 свойств мягкими проверками, внесите 3 падения, проверьте полный отчёт.

Упражнение 3: Playwright Custom Matchers

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