본문 바로가기

Dev.

Playwright, Auth 자동화와 API Mocking

출처 - Lukas의 이미지 of pixels

이 글은 'E2E 테스트로 왜 Playwright 선택했는가?'에 이은 4번째 연재물입니다. 이번 연재물부터는 좀 더 실용적인 지식을 전달해 볼까 하는데요. 사실 소개할 기능들은 너무 많은 것 같습니다. 하지만 그중에 가장 중요하다고 생각되는 2가지 기법을 소개하고자 합니다.

 

 

목차

  • 시작하기 전에 이 2개는 알고 가자!
  • Authorization 자동화
  • API Mocking
  • 마무리

연재물

 

 


시작하기 전에 이 2개는 알고 가자!

E2E 테스트를 구축하면서 아래 2가지 사항은 꼭 고려하게 될겁니다.

 

- Authorization 자동화

- API Mocking

 

playwright는 어떻게 제공하고 있는지 살펴보겠습니다.

 

 

 


Authorization 자동화

대부분의 서비스는 로그인을 해야만 서비스를 이용할 수 있습니다. 각 테스트를 실행할 때마다 로그인부터 시작하게 되는데, 그러면 매 테스트 실행마다 로그인을 해줘야 합니다. 생명주기를 이용하면 되지라고 생각하겠지만, 매번 로그인을 시도하는 건 테스트 속도를 저하시킵니다.

 

딱 한 번만 로그인하면, 그 이후에는 로그인된 채로 테스트할 순 없을까요?

 

playwright는 이런 Authorization 자동화를 위한 기능을 제공해 줍니다. 아래 설명대로 해보세요. 물론 playwright 공식문서(링크)에 이미 다 나와있지만, 좀 더 이해하기 쉽게 설명해 보겠습니다.

 

 

테스트 실행 전 실행

playwright의 configure 에는 globalSetup이라는 옵션이 있습니다. 이 옵션에 파일의 경로를 명시해 두면 모든 테스트 실행 전에 단 한 번 이 파일을 실행시킵니다.

 

playwright.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
});

 


 

LocalStorage에 저장

playwright에서는 LocalStorage 효과를 내는 기능을 제공해 줍니다. 바로 storageState입니다. 덕분에 테스트 전에 한번 로그인만 하면 그 이후는 테스트부터는 다시 Authorization 할 필요가 없어집니다. storageState에 이미 명시되어 있기 때문입니다.

 

멋있죠?! 더 멋진 건 사용법이 꽤 간단합니다. 🤩

 

storageState 코드 작성

global-setup.ts

import { chromium, FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  const { baseURL, storageState } = config.projects[0].use;
 
  const browser = await chromium.launch();
  const page = await browser.newPage();
  
  await page.goto(baseURL!);
  await page.getByLabel('User Name').fill('user');
  await page.getByLabel('Password').fill('password');
  
  await page.getByText('Sign in').click();
  await page.context().storageState({ path: storageState as string });
  await browser.close();
}

export default globalSetup;

 

storageState configure 설정

playwright.config.ts

import { defineConfig } from '@playwright/test';
export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  use: {
    baseURL: 'http://localhost:3000/',
    storageState: 'tests/auth.json',
  },
});

 

너무 간단하죠?!

이런 점 하나하나가 playwright는 잘되어 있는 것 같습니다.  Cypress는 실제 storage에서 데이터를 가져오는 api 기능은 있더라도 이렇게 설정에서 storage와 관련된 편리한 설정은 제공해주지는 않는 것 같습니다. 그렇다는 건 이런 인증 자동화를 직접 다 구현을 해야겠죠? 😖

 

 

 


API Mocking

테스트 코드를 짤 때 유명한 말이 있습니다.

 

테스트는 Mocking 이 반이다!

 

들어보신 적 없나요? 사실 제가 한 말입니다... 🤣

 

테스트를 작성할 때 실제 코드를 대신해서, 가짜 객체나 데이터를 만드는 경우가 있습니다. 이런 작업을 Mocking이라고 표현합니다. 왜 이런 작업이 필요할까요?

 

- 테스트를 통해 얻고자 하는 목표는 단순하고 분명해야 합니다. 🎯

- 모든 걸 테스트할 수 없습니다. 🖐🏻

- 굳이 실행 시간을 기다릴 필요가 없는 경우가 있습니다. 🕐

 

프런트 단에서는 Mocking을 하는 대상 중에 하나가 바로 API입니다. 프런트 입장에서는 벡엔드의 응답값만 알면 됩니다. 벡엔드가 내부적으로 오류가 있든 없든 주 관심사가 아닙니다.

 

'API 널 굳이 테스트하지 않을게, 난 널 믿어!'

 

그렇기 때문에, 어떠한 환경이든 API에서 기대하는 응답값을 보장하기 위해서 Mocking을 해야 할 때가 있습니다. E2E 테스트 시에도 예외는 없습니다.

 

 

Mocking 방법

서론이 길었는데요. 이제 playwright에서 실제로 API를 mocking 하는 방법을 살펴보겠습니다. 공식 문서에 나와있는 코드를 한번 보죠.

 

await page.route('https://dog.ceo/api/breeds/list/all', async route => {
  const json = {
    message: { 'test_breed': [] }
  };
  await route.fulfill({ json });
});

 

문서를 보면 route()는 첫 번째 인자로 넘어간 'https://dog.ceo/api/breeds/list/all' url로 가는 요청을 중간에서 가로챈다고 합니다. 결국 위 코드가 적용되는 테스트에서는 test_bread이 빈 배열로 응답이 오는 거죠.

 

😏 참 쉽쥬?

 

만약 전체 응답값 중에 일부 데이터만 변경하고자 한다면 아래와 같이 route 객체에서. fetch()로 응답값을 받아오고. fulfill()에 함께 넘겨주면 됩니다.

 

await page.route('https://dog.ceo/api/breeds/list/all', async route => {
  const response = await route.fetch();
  const json = await response.json();
  json.message['big_red_dog'] = [];

  await route.fulfill({ response, json });
});

 


 

Mocking 응용

이젠 실전에 적용해 보겠습니다. 상황을 정의해 보겠습니다.

 

- 포스팅 목록 페이지가 있다.

- 각 포스팅은 즐겨찾기 버튼이 있다.

- 즐겨찾기 버튼 클릭 시, postId와 함께 즐겨찾기 API 가 전송된다.

- 이후 로딩 되면서, 즐겨찾기 포스팅이 목록 상단에 보인다.

 

 

저는 이런 상황에서 2가지 API를 Mocking 할 겁니다.

 

- 즐겨찾기 API

- 포스팅 목록 API

 

import { test, expect } from "@playwright/test";
import { posts, bookmarked } from "./fixtures/posts";

test.describe("포스팅 목록", () => {
    test("click 즐겨찾기 버튼", async ({ page }) => {
        await page.locator("//button[text()='즐겨찾기']").click();
        
        await page.route("**/api/v1/bookmarks", route => {
            route.countinue();
        });
        await page.route("**/api/v1/posts", async route => {
            const response = await route.fetch();
            const json = await response.json();
            json.message['posts'] = [bookmarked, ...posts];

            await route.fulfill({ response, json });
        })
       
        const firstPost = await page.locator("tbody tr").first();
  
        expect(firstPost.getByText('개발 스터디')).toBeVisible();
        expect(firstPost.getByText('신청하기')).toBeVisible();
    });
});

 

우선 즐겨찾기 API는 서버 쪽에 데이터가 굳이 가지 않도록 흘려 넘겼습니다.  각 테스트는 독립적이어야 하는데요. API를 통해 서버의 DB 데이터를 변경해버리면, 다음 테스트에 영향이 갈 수 있기 때문입니다.

 

대신, 포스팅 목록을 가져오는 API 를 추가로 mocking 해서, 서버에서 마치 새롭게 변경된 data를 내려준 것처럼 구현했습니다.

 

이게 무슨 의미가 있겠나 싶으신 분도 있을 것 같습니다.

 

 

하지만, 이 작업은 2가지 의미가 있습니다!!

- event 발생 이후 어떤 변화를 테스트하고 싶냐에 따라 의미가 달라질 수 있습니다.
- 테스트를 보고, 시나리오를 이해할 수 있습니다.

 

 

 


마무리

지금까지 playwright에서 Authorization 자동화와 API를 mocking 하는 방법을 살펴봤습니다. 사실 어렵지 않은 개념이지만 테스트에 대한 개념이 익숙하지 않으면 뭘 어떻게 해야 할지 모를 수 있습니다.

 

하지만 더 쾌적하고 독립적인 테스트 환경을 구축하는데, 소개드린 두 가지는 꼭 필요한 기법들이라고 생각합니다.

 

특히 mocking은 API 뿐만 아니라, npm 라이브러리도 대상이 됩니다. 물론! E2E에서는 외부 모듈까지 mocking 할 필요는 없어 보입니다.

하지만 우리가 Unit test를 진행할 때, 꼭 한 번씩 하게 되는 게 npm 라이브러리를 mocking 하는 작업입니다. 다음에는 유명한 testing runner인 jest도 살펴볼 건데요. jest에서 mocking을 어떻게 하는지, 정리해보도록 하겠습니다.

 

 

 

https://blog.delpuppo.net/playwright-mock-api

 

Playwright - Mock API

Mock API

blog.delpuppo.net

https://playwright.dev/docs/test-global-setup-teardown#configure-globalsetup-and-globalteardown

 

Global setup and teardown | Playwright

There are two ways to configure global setup and teardown: using a global setup file and setting it in the config under globalSetup or using project dependencies. With project dependencies, you define a project that runs before all other projects. This is

playwright.dev

 

 
 

 
아래는 개발 커뮤니티 링크입니다. 들어오셔서 기술 정보, 트렌드 정보, 유료스터디, 무료스터디, 모각코 등등을 이용해 보세요.