๐Ÿง‘‍๐Ÿ’ป Web/React

[React] Cypress๋กœ e2e ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž

Lennon 2023. 5. 14. 19:28
728x90
๋ฐ˜์‘ํ˜•

Next.js๋ฅผ ํฌํ•จํ•œ React ํ”„๋กœ์ ํŠธ์—์„œ Cypress๋กœ e2e๋ฅผ ๊ตฌํ˜„ํ•ด ๋ด…์‹œ๋‹ค.

 

ํ”„๋กœ์ ํŠธ์— Cypress๋ฅผ ์„ค์น˜ํ•ด ์ค๋‹ˆ๋‹ค.

$ yarn add cypress --dev
or
$ npm install cypress --save-dev

 

Package.json์— ๊ฐ€์„œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค๋‹ˆ๋‹ค.

    "scripts": {
        ...,
        "test": "cypress open",
        "cy:run": "cypress run"
    },

ํ„ฐ๋ฏธ๋„์—์„œ ๋ช…๋ น์–ด๋ฅผ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค

$ yarn test 
or
$ npm run test

 

๊ทธ๋Ÿผ ์•„๋ž˜์™€ ๊ฐ™์€ ํ”„๋กœ๊ทธ๋žจ์ด ์ผœ์งˆ ๊ฒ๋‹ˆ๋‹ค!

 

์šฐ๋ฆฌ๋Š” e2e๋ฅผ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๋‹ˆ๊นŒ ์ฒซ ๋ฒˆ์งธ๋ฅผ ์„ ํƒํ•˜๊ณ  ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด ์ค€๋‹ค๋Š” ๊ฑฐ์— ๋™์˜ํ•˜๊ณ  ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค.

์ œ ๊ธฐ์ค€ Electron์ด ํŽธํ•ด์„œ ์„ ํƒํ•˜์—ฌ Start๋ฅผ ๋ˆ„๋ฆ…๋‹ˆ๋‹ค.

 

โ›”๏ธ ์ด ๊ณผ์ •์—์„œ ํฌ๋กฌ์ด๋‚˜ Electron์„ ์ผœ๋ฉด ๋นˆ ํ™”๋ฉด(blank screen)์ด ๋œจ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š”๋ฐ 
์ฐธ๊ณ  (https://github.com/cypress-io/cypress/issues/4131) ์œ„ ๊ธ€์—์„œ๋Š” ์›น์†Œ์ผ“ ๋ฌธ์ œ, ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ฌธ์ œ ๋“ฑ๋“ฑ์„ ์–ธ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” ๊ณต์‹๋ฌธ์„œ๋งŒ ๋ณด๊ณ  ๋”ฐ๋ผ ํ–ˆ์„ ๋ฟ์ธ๋ฐ ์•ˆ ๋ผ์„œ ๊ทผ๋ณธ์ ์ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋ผ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

yarn test๋ฅผ ์‹คํ–‰ํ•œ ํ„ฐ๋ฏธ๋„์—์„œ ์˜ค๋ฅ˜๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋Š”๋ฐ ํ•ด๋‹น ํ˜„์ƒ์ด ๊ณ„์† ์ง€์†๋˜์—ˆ์ง€๋งŒ, ๋†€๋ž๊ฒŒ๋„ ๋งฅ๋ถ ์žฌ๋ถ€ํŒ…ํ•˜๋‹ˆ๊นŒ ํ•ด๊ฒฐ์ด ๋์Šต๋‹ˆ๋‹ค.

โœ… ์ปดํ“จํ„ฐ ์žฌ๋ถ€ํŒ…์„ ํ•œ ๋ฒˆ ํ•ด๋ณด์„ธ์š”!

 

๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€๋ด…์‹œ๋‹ค.

 

cypress.config.ts์™€ ์ „์—ญ ํด๋”์— cypress๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์„ ๊ฒ๋‹ˆ๋‹ค.

์ €๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

// cypress.config.ts

import { defineConfig } from 'cypress';

export default defineConfig({
    projectId: process.env.CYPRESS_PROJECT_ID,
    e2e: {
        baseUrl: 'http://localhost:3000',
        setupNodeEvents(on, config) {
            // implement node event listeners here
        }
    },
    chromeWebSecurity: false
});

 

cypress ํด๋” ๋‚ด๋ถ€์— e2e ํด๋”๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.

 

e2e ํด๋”์—์„œ ๊ฐ์ž ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋“ค์„ ์ง€์ •ํ•ด ์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

// cypress/e2e/signIn.cy.ts

describe('SignIn', () => {
    beforeEach(() => {
        cy.visit('/auth/signIn');
    });

    it('should signIn successfully', () => {
        cy.get('[type="text"]').type('test@test.com');

        cy.get('[type="password"]').type('testUser123');

        cy.contains('๋กœ๊ทธ์ธ').click();

        cy.url().should('include', '/main');
    });

    it('should navigate to sign up page successfully', () => {
        cy.contains('ํšŒ์›๊ฐ€์ž…').click();

        cy.url().should('include', '/auth/signUp');
    });
});

๊ฐ„๋‹จํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

cypress.config.ts์— baseUrl์„ ์ง€์ •ํ•ด ์ค˜์„œ pathname๋งŒ ์ง€์ •ํ•ด ์ค˜๋„ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

 

beforeEach() ๋‚ด๋ถ€์˜ ์ •์˜ํ•œ ๊ฑด ํ•ด๋‹น describe์— ์ •์˜๋œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋“ค์ด ๋ชจ๋‘ ๊ณต์œ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ฆ‰ ๋ชจ๋‘ ์‹œ์ž‘ํ•  ๋•Œ /auth/signIn์„ ๋ฐฉ๋ฌธํ•˜๊ฒ ์ฃ ?

 

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

describe('invalid inputs', () => {
        beforeEach(() => {
            cy.get('[id="nickname"]').type('test');
            cy.get('[id="email"]').type('test@naver.com');
            cy.get('[id="password"]').type('testUser123');
            cy.get('[id="password_check"]').type('testUser123');
        });

        it('should display an error message when nickname is too short', () => {
            cy.get('[id="nickname"]').clear().type('h');
            cy.contains('๊ฐ€์ž…ํ•˜๊ธฐ').should('be.disabled');
        });

        it('should display an error message when email is invalid', () => {
            cy.get('[id="email"]').clear().type('testnaver.com');
            cy.contains('๊ฐ€์ž…ํ•˜๊ธฐ').should('be.disabled');
        });

        it('should display an error message when password is too short', () => {
            cy.get('[id="password"]').clear().type('1234');
            cy.contains('๊ฐ€์ž…ํ•˜๊ธฐ').should('be.disabled');
        });

        it('should display an error message when match check password is not same', () => {
            cy.get('[id="password_check"]').clear().type('testUser124');
            cy.contains('๊ฐ€์ž…ํ•˜๊ธฐ').should('be.disabled');
        });
    });

๋ณธ์ธ์ด ํ•„์š”ํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๋‹ค์‹œ Cypress ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ๊ฐ€๋ณด๋ฉด

์•„๋ž˜์™€ ๊ฐ™์ด E2E specs์— ์ •์˜ํ•œ ํ…Œ์ŠคํŠธ๋“ค์ด ์ •์˜๋˜์–ด ์žˆ์„ ๊ฑฐ์˜ˆ์š”.

 

์—ฌ๊ธฐ์„œ ํด๋ฆญํ•˜๋ฉด ์•„๋ž˜ ์˜์ƒ์ฒ˜๋Ÿผ GUI๋กœ ๋น„๋””์˜ค ๋ฐ ์Šค๋ƒ…์ˆ์„ ๋ณด๋ฉด์„œ ๋ฐ”๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

CLI๋กœ ํ•˜๋ ค๋ฉด ์œ„์—์„œ ์ •์˜ํ•œ cy:run ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

$ yarn run cy:run -- --record --spec "cypress/e2e";
or
$ npm run cy:run -- --record --spec "cypress/e2e";

์œ„ ๋ช…๋ น์–ด๋ฅผ ์ž‘์„ฑํ•˜๋ฉด e2e ๋‚ด๋ถ€์— ์žˆ๋Š” ๋ชจ๋“  ํ…Œ์ŠคํŠธ๋“ค์„ ์‹œ๋„ํ•˜์—ฌ ํ„ฐ๋ฏธ๋„์— ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ค๋‹ˆ๋‹ค.

 

๊ฐœ์ธ์ ์œผ๋กœ Cypress ํ”„๋กœ๊ทธ๋žจ์—์„œ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๋ณด๋‹จ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒŒ ์ข‹์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

CLI์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹œ๋„ํ•˜๋ฉด Cypress์•ˆ์— screenshots, videos ํด๋”์— ๋ฐ์ดํ„ฐ๋“ค์ด ์Œ“์ด๋Š”๋ฐ ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ผ๊ฐ€์ง€ ์•Š๋„๋ก 

. gitignore์— ์ถ”๊ฐ€ํ•ด ์ค์‹œ๋‹ค

# cypress video, screenshots

cypress/videos
cypress/screenshots

์•„๋ž˜ PR์— e2e ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ์ ์–ด๋†”์„œ ์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

https://github.com/Nimble-Meet/client/pull/28

 

feat(e2e): e2e test ๊ตฌํ˜„ by pmhxhsj · Pull Request #28 · Nimble-Meet/client

cypress๋ฅผ ์ด์šฉํ•ด e2e ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. โ›”๏ธ ์ถ”ํ›„ ์ปจ๋ฒค์…˜ ํ™•๋ฆฝ ๋ฐ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง ํ›„ merge ์˜ˆ์ •

github.com

 
 
728x90
๋ฐ˜์‘ํ˜•