10.2 Test Step과 Boxed Step
test.step()은 report와 trace에서 business action을 묶습니다. 좋은 step은 구현 detail을 감추되 실패 원인을 숨기지 않습니다.
기본 step
test('Todo lifecycle', async ({ page }) => {
await test.step('할 일을 만든다', async () => {
await page.getByLabel('할 일', { exact: true }).fill('Architecture 학습');
await page.getByRole('button', { name: '추가' }).click();
});
await test.step('완료 상태를 검증한다', async () => {
const item = page.getByRole('listitem').filter({ hasText: 'Architecture 학습' });
await item.getByRole('checkbox').check();
await expect(item.getByRole('checkbox')).toBeChecked();
});
});
“click input”, “fill text”처럼 API 한 줄마다 step을 만들면 report가 길어지고 business intent가 사라집니다.
Boxed step
box: true는 step 안의 low-level failure를 호출 지점의 business step으로 표현합니다.
async function createTodo(page: Page, title: string) {
return test.step(`Todo 생성: ${title}`, async () => {
await page.getByLabel('할 일', { exact: true }).fill(title);
await page.getByRole('button', { name: '추가' }).click();
}, { box: true });
}
Boxing은 page object 내부 detail이 report를 압도할 때 유용하지만, trace와 source link에서 실제 실패 action을 여전히 찾을 수 있어야 합니다.
Page Object decorator pattern
공식 microsoft/playwright-examples에는 TypeScript decorator로 page object method를 boxed step으로 감싸는 예제가 있습니다. 팀이 decorator syntax와 compiler 설정을 이해하지 못하면 단순 helper가 더 유지하기 쉽습니다.
function boxedStep(target: Function, context: ClassMethodDecoratorContext) {
return function replacement(this: object, ...args: unknown[]) {
const name = `${this.constructor.name}.${String(context.name)}`;
return test.step(name, () => target.call(this, ...args), { box: true });
};
}
Attachment와 step 결합
await test.step('API 결과를 기록한다', async () => {
const response = await page.request.get('/api/summary');
const body = await response.json();
await test.info().attach('summary.json', {
body: JSON.stringify(body, null, 2),
contentType: 'application/json',
});
});
실습
- 기존 Todo test를 세 business step으로 나눕니다.
- Low-level action마다 step을 만든 report와 비교합니다.
- Page object method 하나를 boxed step으로 감쌉니다.
- 의도적으로 locator를 깨고 source location과 trace를 찾아갑니다.