Skip to content
Migrating from NextAuth.js v4? Read our migration guide.
가이드Testing

테스트

인증 기능을 반복적이고 일관성 있게 테스트하는 것은 항상 까다로운 작업이었습니다. 특히 OAuth 프로바이더는 자동화된 방식으로 테스트하기가 더 어려운데, 새로운 지리적 위치, 데이터센터 IP 주소, 새로운 사용자 에이전트 등에서 로그인할 경우 추가적인 검증 단계가 발생하기 때문입니다.

이러한 제약을 극복하기 위해 Auth.js 애플리케이션에 대한 성공적인 E2E 테스트를 실행할 수 있는 전략을 소개합니다.

  1. Keycloak과 같은 소프트웨어를 사용해 직접 OAuth 프로바이더를 실행
  2. 개발 모드에서 Credentials 프로바이더와 같은 인증 방법 활성화

아래는 각 전략의 예시로, 자동화된 E2E 테스트를 위해 @playwright/test를 활용한 방법입니다.

Keycloak

먼저 Keycloak 인스턴스를 설정합니다. 그런 다음 Auth.js 설정에 Keycloak 프로바이더를 추가해야 합니다.

이 테스트를 실행하려면 두 개의 환경 변수를 설정해야 합니다. 이 자격 증명은 새로 생성한 Keycloak 인스턴스에 대해 인증할 수 있는 테스트 사용자의 정보여야 합니다.

.env
TEST_KEYCLOAK_USERNAME=abc
TEST_KEYCLOAK_PASSWORD=123

그런 다음 @playwright/test를 사용하여 두 가지 테스트 단계를 실행할 수 있습니다.

  1. 로그인 URL을 방문하고, 인증 자격 증명을 입력한 다음 “Sign In” 버튼을 클릭합니다. 이 단계는 세션이 올바르게 설정되었는지 확인합니다.
  2. “Sign Out” 버튼을 클릭하고 세션이 null로 설정되었는지 확인합니다.
tests/e2e/basic-auth.spec.ts
import { test, expect, type Page } from "@playwright/test"
 
test("Basic auth", async ({ page, browser }) => {
  if (
    !process.env.TEST_KEYCLOAK_USERNAME ||
    !process.env.TEST_KEYCLOAK_PASSWORD
  )
    throw new TypeError("Incorrect TEST_KEYCLOAK_{USERNAME,PASSWORD}")
 
  await test.step("should login", async () => {
    await page.goto("http://localhost:3000/auth/signin")
    await page.getByText("Keycloak").click()
    await page.getByText("Username or email").waitFor()
    await page
      .getByLabel("Username or email")
      .fill(process.env.TEST_KEYCLOAK_USERNAME)
    await page.locator("#password").fill(process.env.TEST_KEYCLOAK_PASSWORD)
    await page.getByRole("button", { name: "Sign In" }).click()
    await page.waitForURL("http://localhost:3000")
    const session = await page.locator("pre").textContent()
 
    expect(JSON.parse(session ?? "{}")).toEqual({
      user: {
        email: "bob@alice.com",
        name: "Bob Alice",
        image: "https://avatars.githubusercontent.com/u/67470890?s=200&v=4",
      },
      expires: expect.any(String),
    })
  })
 
  await test.step("should logout", async () => {
    await page.getByText("Sign out").click()
    await page
      .locator("header")
      .getByRole("button", { name: "Sign in", exact: true })
      .waitFor()
    await page.goto("http://localhost:3000/auth/session")
 
    expect(await page.locator("html").textContent()).toBe("null")
  })
})

개발 환경에서의 Credentials 프로바이더

이 방법은 별도의 OAuth 프로바이더(예: Keycloak)를 유지할 필요가 없어 초기 설정과 유지보수가 간단하지만, 프로덕션 환경에서 안전하지 않은 인증 방법을 남겨두지 않도록 각별히 주의해야 합니다. 예를 들어, 이 예제에서는 password라는 비밀번호를 허용하는 Credentials 프로바이더를 추가할 것입니다.

auth.ts
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
import Credentials from "next-auth/providers/credentials"
 
const providers = [GitHub]
 
if (process.env.NODE_ENV === "development") {
  providers.push(
    Credentials({
      id: "password",
      name: "Password",
      credentials: {
        password: { label: "Password", type: "password" },
      },
      authorize: (credentials) => {
        if (credentials.password === "password") {
          return {
            email: "bob@alice.com",
            name: "Bob Alice",
            image: "https://avatars.githubusercontent.com/u/67470890?s=200&v=4",
          }
        }
      },
    })
  )
}
 
export const { handlers, auth } = NextAuth({
  providers,
})

위의 설정 예제는 항상 GitHub 프로바이더를 추가하고, Credentials 프로바이더는 개발 환경에서만 추가합니다. 이 설정을 조정한 후, 앞서와 동일한 방식으로 @playwright/test 테스트를 작성할 수 있습니다.

tests/e2e/basic-auth.spec.ts
import { test, expect, type Page } from "@playwright/test"
 
test("Basic auth", async ({ page, browser }) => {
  if (!process.env.TEST_PASSWORD) throw new TypeError("Missing TEST_PASSWORD")
 
  await test.step("should login", async () => {
    await page.goto("http://localhost:3000/auth/signin")
    await page.getByLabel("Password").fill(process.env.TEST_PASSWORD)
    await page.getByRole("button", { name: "Sign In" }).click()
    await page.waitForURL("http://localhost:3000")
    const session = await page.locator("pre").textContent()
 
    expect(JSON.parse(session ?? "{}")).toEqual({
      user: {
        email: "bob@alice.com",
        name: "Bob Alice",
        image: "https://avatars.githubusercontent.com/u/67470890?s=200&v=4",
      },
      expires: expect.any(String),
    })
  })
 
  await test.step("should logout", async () => {
    await page.getByText("Sign out").click()
    await page
      .locator("header")
      .getByRole("button", { name: "Sign in", exact: true })
      .waitFor()
    await page.goto("http://localhost:3000/auth/session")
 
    expect(await page.locator("html").textContent()).toBe("null")
  })
})
Auth.js © Balázs Orbán and Team - 2025