NextJS: 테스팅

hwisaac·2023년 3월 13일
0

Next.js

목록 보기
21/29

테스팅

예시:

이 문서에서는 Cypress, Playwright, JestReact Testing Library와 같은 대표적인 테스팅 툴을 사용하여 Next.js를 어떻게 설정하는지 배울 수 있습니다.

Cypress

CypressEnd-to-End (E2E)Component Testing에 사용되는 테스트 러너입니다.

빠른 시작

with-cypress 예제를 사용하여 create-next-app을 실행하여 빠르게 시작할 수 있습니다.

npx create-next-app@latest --example with-cypress with-cypress-app

수동 설정

Cypress를 시작하려면 cypress 패키지를 설치합니다.

npm install --save-dev cypress

Cypresspackage.json의 스크립트 필드에 추가합니다.

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "cypress": "cypress open",
}

Cypress를 처음 실행하여 권장되는 폴더 구조를 사용하는 예제를 생성합니다.

npm run cypress

생성된 예제와 Cypress 문서의 Writing Your First Test 섹션을 확인하여 Cypress에 익숙해질 수 있습니다.

E2E 테스트와 컴포넌트 테스트 중 어떤 것을 사용해야 할까요?

Cypress 문서에는 이 두 가지 유형의 테스트의 차이점과 언제 어떤 것을 사용하는 것이 적절한지에 대한 가이드가 포함되어 있습니다.

Cypress E2E 테스트 생성하기

다음과 같은 두 개의 Next.js 페이지가 있다고 가정해보겠습니다:

// pages/index.js
import Link from 'next/link'

export default function Home() {
  return (
    <nav>
      <h1>Homepage</h1>
      <Link href="/about">About</Link>
    </nav>
  )
}
// pages/about.js
export default function About() {
  return (
    <div>
      <h1>About Page</h1>
      <Link href="/">Homepage</Link>
    </div>
  )
}

다음 테스트를 추가하여 내비게이션이 올바르게 작동하는지 확인할 수 있습니다:

// cypress/e2e/app.cy.js

describe('Navigation', () => {
  it('should navigate to the about page', () => {
    // 인덱스 페이지에서 시작
    cy.visit('http://localhost:3000/')

    // "about"을 포함하는 href 속성을 가진 링크를 찾아 클릭
    cy.get('a[href*="about"]').click()

    // 새로운 URL은 "/about"을 포함해야 합니다.
    cy.url().should('include', '/about')

    // 새 페이지에 "About page"를 포함하는 h1이 있어야 합니다.
    cy.get('h1').contains('About Page')
  })
})

cypress.config.js 구성 파일에 baseUrl: 'http://localhost:3000'을 추가하면 cy.visit("http://localhost:3000/") 대신 cy.visit("/")을 사용할 수 있습니다.

Cypress 컴포넌트 테스트 생성하기

컴포넌트 테스트는 전체 애플리케이션을 번들링하거나 서버를 시작하지 않고 특정 컴포넌트를 빌드하고 마운트합니다. 이를 통해 더 빠르고 성능적으로 효율적인 테스트를 수행할 수 있습니다. 이러한 컴포넌트 테스트는 시각적 피드백과 Cypress E2E 테스트에 사용되는 동일한 API를 제공합니다.

참고: 컴포넌트 테스트는 Next.js 서버를 시작하지 않으므로, 서버가 필요한 <Image />getServerSideProps와 같은 기능은 기본적으로 작동하지 않습니다. 이러한 기능을 컴포넌트 테스트에서 사용하는 방법에 대한 예는 Cypress Next.js 문서를 참조하십시오.

이전 섹션과 동일한 컴포넌트를 가정하고, 예상된 출력이 렌더링되는지 확인하는 테스트를 추가해 보겠습니다:

// pages/about.cy.js
import AboutPage from './about.js'

describe('<AboutPage />', () => {
  it('should render and display expected content', () => {
    // About 페이지의 React 컴포넌트를 마운트합니다.
    cy.mount(<AboutPage />)

    // 새 페이지에 "About page"를 포함하는 h1이 있어야 합니다.
    cy.get('h1').contains('About Page')

    // 예상된 URL을 가진 링크가 존재하는지 확인합니다.
    // *링크를 따라가는* 것은 E2E 테스트에 더 적합합니다.
    cy.get('a[href="/"]').should('be.visible')
  })
})

Cypress 테스트 실행하기

E2E Tests

Cypress E2E 테스트는 실제 Next.js 애플리케이션을 테스트하기 때문에 Cypress를 시작하기 전에 Next.js 서버가 실행되어야 합니다. 애플리케이션이 어떻게 작동할지 더욱 가까이 모사하기 위해 프로덕션 코드를 대상으로 테스트하는 것을 권장합니다.

npm run buildnpm run start를 실행한 다음에 다른 터미널 창에서 npm run cypress -- --e2e를 실행하여 Cypress를 시작하고 E2E 테스트 스위트를 실행하세요.

참고: start-server-and-test 패키지를 설치하고 package.json 스크립트 필드에 추가할 수도 있습니다. "test": "start-server-and-test start http://localhost:3000 cypress"Cypress와 함께 Next.js 프로덕션 서버를 시작하는 데 사용됩니다. 새로운 변경 후에 애플리케이션을 다시 빌드해야 합니다.

Component Tests

npm run cypress -- --component를 실행하여 Cypress를 시작하고 컴포넌트 테스트 스위트를 실행하세요.

CI (지속적인 통합) 준비하기

Cypress를 실행하면 대화형 브라우저가 열리기 때문에 CI 환경에서는 이상적이지 않습니다. cypress run 명령어를 사용하여 Cypressheadless 모드로 실행할 수도 있습니다:

// package.json

"scripts": {
  //...
  "e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"",
  "e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"",
  "component": "cypress open --component",
  "component:headless": "cypress run --component"
}

Cypress와 지속적인 통합에 관한 자세한 내용은 다음 자료를 참고하세요.

Playwright

Playwright는 하나의 API로 Chromium, Firefox, WebKit을 자동화할 수 있는 테스트 프레임워크입니다. 이를 사용하여 모든 플랫폼에서 End-to-End (E2E) 및 통합 테스트를 작성할 수 있습니다.

빠른 시작

가장 빠르게 시작하는 방법은 with-playwright 예제를 사용하여 create-next-app을 사용하는 것입니다. 이렇게 하면 Playwright가 설정된 Next.js 프로젝트가 생성됩니다.

npx create-next-app@latest --example with-playwright with-playwright-app

수동 설정

기존 NPM 프로젝트에 Playwright를 추가하려면 npm init playwright을 사용할 수도 있습니다.

Playwright를 수동으로 시작하려면 @playwright/test 패키지를 설치하세요.

npm install --save-dev @playwright/test

Playwrightpackage.json 스크립트 필드에 추가하세요.

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "test:e2e": "playwright test",
}

첫 번째 Playwright End-to-End 테스트 작성하기
다음과 같은 두 개의 Next.js 페이지를 가정합니다.

// pages/index.js
import Link from 'next/link'

export default function Home() {
  return (
    <nav>
      <Link href="/about">About</Link>
    </nav>
  )
}
// pages/about.js
export default function About() {
  return (
    <div>
      <h1>About Page</h1>
    </div>
  )
}

다음 내용을 검증하는 테스트를 추가하세요: 네비게이션 기능이 제대로 작동하는지 검증합니다.

// e2e/example.spec.ts

import { test, expect } from '@playwright/test'

test('should navigate to the about page', async ({ page }) => {
  // index 페이지에서 시작합니다. (playwright.config.ts에서 webServer를 통해 baseURL을 설정합니다.)
  await page.goto('http://localhost:3000/')
  // 'About Page' 텍스트가 포함된 요소를 찾고 클릭합니다.
  await page.click('text=About')
  // 새 URL은 "/about" 이어야 합니다. (baseURL이 사용됩니다.)
  await expect(page).toHaveURL('http://localhost:3000/about')
  // 새 페이지에는 "About Page" h1이 있어야 합니다.
  await expect(page.locator('h1')).toContainText('About Page')
})

playwright.config.ts 구성 파일에 "baseURL": "http://localhost:3000"을 추가하면 page.goto("/") 대신 page.goto("http://localhost:3000/")을 사용할 수 있습니다.

Playwright 테스트 실행하기

Playwright는 실제 Next.js 애플리케이션을 테스트하기 때문에, Playwright를 시작하기 전에 Next.js 서버가 실행 중이어야합니다. 애플리케이션이 동작하는 방식과 더욱 유사하게 테스트하기 위해 프로덕션 코드에 대해 테스트하는 것이 좋습니다.

터미널에서 npm run buildnpm run start를 실행한 다음, 다른 터미널 창에서 npm run test:e2e를 실행하여 Playwright 테스트를 실행할 수 있습니다.

참고: 대안으로, Playwright가 개발 서버를 시작하고 사용 가능할 때까지 대기하도록 webServer 기능을 사용할 수 있습니다.

Continuous Integration(CI)에서 Playwright 실행하기
Playwright는 기본적으로 헤드리스 모드(headless mode)에서 테스트를 실행합니다. 모든 Playwright 종속성을 설치하려면 npx playwright install-deps를 실행하세요.

다음 리소스에서 Playwright 및 CI에 대해 더 알아볼 수 있습니다.

Jest and React Testing Library

Jest와 React Testing Library유닛 테스트를 위해 자주 함께 사용됩니다. Next.js 애플리케이션에서 Jest를 사용하는 방법에는 다음과 같은 세 가지가 있습니다:

퀵스타트

with-jest 예제를 사용하여 create-next-app을 실행하면 쉽게 JestReact Testing Library를 시작할 수 있습니다:

npx create-next-app@latest --example with-jest with-jest-app

Jest 설정하기 (Rust 컴파일러로)

Next.js 12부터 Jest를 위한 내장 구성이 추가되었습니다.

Jest를 설정하려면 jest, jest-environment-jsdom, @testing-library/react, @testing-library/jest-dom을 설치하세요:

npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom

프로젝트 루트 디렉토리에 jest.config.js 파일을 만들고 다음과 같이 추가하세요:

// jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  // 테스트 환경에서 next.config.js 및 .env 파일을 로드하기 위해 Next.js 앱의 경로를 제공합니다
  dir: './',
})

// Jest에 전달할 사용자 정의 구성 추가
/** @type {import('jest').Config} */
const customJestConfig = {
  // 각 테스트가 실행되기 전에 추가 설정 옵션을 더할 수 있습니다
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],

  testEnvironment: 'jest-environment-jsdom',
}

// createJestConfig은 next/jest에서 Next.js 구성을 비동기적으로 로드할 수 있도록 이렇게 내보내집니다
module.exports = createJestConfig(customJestConfig)

내부적으로, next/jest는 자동으로 다음과 같은 작업을 수행하여 Jest를 구성합니다:

  • SWC를 사용하여 변환 설정
  • 스타일시트(.css, .module.css, scss 변형) 자동 모킹, 이미지 가져오기 및 next/font
  • .env(및 모든 변형)를 process.env에 로드
    node_modules를 테스트 해결 및 변환에서 제외
  • .next를 테스트에서 제외
  • SWC 변환을 활성화하는 플래그가 포함된 next.config.js를 로드합니다.

참고: 환경 변수를 직접 테스트하려면 별도의 설정 스크립트나 jest.config.js 파일에서 수동으로 로드해야 합니다. 자세한 정보는 Test Environment Variables을 참조하세요.

Jest (with Babel) 설정하기

Rust 컴파일러를 사용하지 않을 경우, Jest를 수동으로 구성하고 babel-jestidentity-obj-proxy를 설치해야 합니다. 다음은 Next.js에서 Jest를 구성하는 권장 옵션입니다.

// jest.config.js
module.exports = {
  collectCoverage: true,
  // node 14.x에서 커버리지 제공자 v8은 속도가 좋고 보고서도 거의 좋음
  coverageProvider: 'v8',
  collectCoverageFrom: [
    '**/*.{js,jsx,ts,tsx}',
    '!**/*.d.ts',
    '!**/node_modules/**',
    '!<rootDir>/out/**',
    '!<rootDir>/.next/**',
    '!<rootDir>/*.config.js',
    '!<rootDir>/coverage/**',
  ],
  moduleNameMapper: {
    // CSS import 처리(CSS 모듈 포함)
    // https://jestjs.io/docs/webpack#mocking-css-modules
    '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',

    // CSS import 처리(CSS 모듈 미포함)
    '^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',

    // 이미지 import 처리
    // https://jestjs.io/docs/webpack#handling-static-assets
    '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`,

    // 모듈 별칭 처리
    '^@/components/(.*)$': '<rootDir>/components/$1',
  },
  // 테스트가 실행되기 전에 추가적인 설정 옵션을 추가
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
  testEnvironment: 'jsdom',
  transform: {
    // next/babel 프리셋을 사용하여 babel-jest를 이용하여 테스트를 트랜스파일
    // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object
    '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
  },
  transformIgnorePatterns: [
    '/node_modules/',
    '^.+\\.module\\.(css|sass|scss)$',
  ],
}

Jest 설정 옵션에 대해 자세히 알아보려면 Jest 문서를 참조하세요.

스타일시트 및 이미지 import 처리

테스트에서는 스타일시트와 이미지가 사용되지 않지만, 이를 가져오는 것이 오류를 유발할 수 있으므로 모킹해야 합니다.

위에서 구성에 참조된 파일

  • fileMock.jsstyleMock.js__mocks__ 디렉토리 내에 생성하세요.
// __mocks__/fileMock.js
module.exports = {
  src: '/img.jpg',
  height: 24,
  width: 24,
  blurDataURL: 'data:image/png;base64,imagedata',
}
// __mocks__/styleMock.js
module.exports = {}

정적 자산 처리에 대한 자세한 정보는 Jest 문서를 참조하세요.

선택 사항: 사용자 정의 매처로 Jest 확장하기

@testing-library/jest-dom.toBeInTheDocument()와 같은 편리한 사용자 정의 매처 세트를 제공하여 테스트 작성을 용이하게 해줍니다. Jest 구성 파일에 다음 옵션을 추가하여 모든 테스트에서 사용자 정의 매처를 가져올 수 있습니다.

// jest.config.js
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']

그런 다음 jest.setup.js 내에 다음 import를 추가하세요.

// jest.setup.js
import '@testing-library/jest-dom/extend-expect'

각 테스트가 실행되기 전에 추가 설정 옵션을 추가해야하는 경우, 보통 jest.setup.js 파일에 추가합니다.

선택 사항: 절대 경로 및 모듈 별칭

모듈 별칭을 사용하는 경우, Jest를 구성하여 jsconfig.json 파일의 paths 옵션과 jest.config.js 파일의 moduleNameMapper 옵션을 일치시켜 가져올 수 있도록 해야합니다. 예를 들어:

// tsconfig.json 또는 jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/components/*": ["components/*"]
    }
  }
}
// jest.config.js
moduleNameMapper: {
  '^@/components/(.*)$': '<rootDir>/components/$1',
}

테스트 작성하기:

package.json에 테스트 스크립트 추가
Jest 실행 파일을 package.json 스크립트에 추가하여 watch 모드로 실행합니다:

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "test": "jest --watch"
}

jest --watch는 파일이 변경될 때마다 테스트를 다시 실행합니다. 더 많은 Jest CLI 옵션에 대해서는 Jest 문서를 참조하세요.

첫 번째 테스트 작성하기

이제 테스트를 작성해 볼까요? 테스트를 작성하기 전에, npm run dev 명령어를 사용하여 Next.js 앱을 실행하고, 앱이 응답하는지 확인해야 합니다. 이것은 앱이 정상적으로 실행 중이며, 테스트 작성이 가능함을 나타냅니다.

테스트 파일은 __tests__ 디렉토리에 저장하는 것이 좋습니다. 이것은 Jest가 자동으로 인식하고 실행할 수 있는 디렉토리입니다. 예를 들어, 다음과 같은 파일 구조를 가진 pages/index.js 파일이 있다고 가정해 봅시다.

import styles from '../styles/Home.module.css'

export default function Home() {
  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>
        <p className={styles.description}>
          Get started by editing <code className={styles.code}>pages/index.js</code>
        </p>
      </main>
    </div>
  )
}

이제 __tests__ 디렉토리에 index.test.js 파일을 만들어 보겠습니다.

import { render, screen } from '@testing-library/react'
import Home from '../pages/index'

describe('Home', () => {
  it('renders a heading', () => {
    render(<Home />)
    const heading = screen.getByRole('heading', { name: /welcome to next\.js!/i })
    expect(heading).toBeInTheDocument()
  })
})

이 코드는 Home 컴포넌트가 <h1> 요소를 렌더링하는지 확인하는 단순한 테스트를 수행합니다. render 함수를 사용하여 Home 컴포넌트를 렌더링하고, screen 객체를 사용하여 DOM 요소를 가져옵니다. 마지막으로 expect 함수를 사용하여 DOM 요소가 실제로 존재하는지 확인합니다.

테스트를 실행하려면, npm run test 명령어를 실행하면 됩니다. 테스트 결과가 출력됩니다. 이제 Welcome to Next.js! 텍스트가 표시되는지 확인하는 테스트가 통과했음을 확인할 수 있습니다.

Snapshot 테스트 작성하기

예상치 못한 변경사항이 있는지 추적하기 위해 <Home /> 컴포넌트의 스냅샷 테스트를 추가할 수도 있습니다.

// __tests__/snapshot.js

import { render } from '@testing-library/react'
import Home from '../pages/index'

it('renders homepage unchanged', () => {
  const { container } = render(<Home />)
  expect(container).toMatchSnapshot()
})

참고: 테스트 파일은 pages 디렉터리 내에 포함되어서는 안됩니다. pages 디렉터리 내의 모든 파일은 라우트로 간주되기 때문입니다.

테스트 스위트 실행하기

npm run test 명령어를 실행하여 테스트 스위트를 실행할 수 있습니다. 테스트가 통과하거나 실패한 후에는 더 많은 테스트를 추가하는 데 도움이 될 인터랙티브한 Jest 명령어 목록이 표시됩니다.

더 많은 자료를 원한다면, 아래 자료를 참고해보세요.

커뮤니티 패키지와 예제

Next.js 커뮤니티에서는 유용한 패키지와 문서를 만들어 두었습니다.

0개의 댓글