[Jest + POM] ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•œ ํ…Œ์ŠคํŠธ ๋ฆฌํŒฉํ† ๋ง

2025. 4. 20. 17:36ยท๐Ÿ“‚ Quality Assurance Study/๐Ÿ“„ QA ๊ณต๋ถ€

๐Ÿ“Œ ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ

VSCode/
โ”œโ”€โ”€ JavaScript_Prac/
โ”‚      โ”œโ”€โ”€ LoginPage.js
โ”‚      โ””โ”€โ”€ LoginPage.test.js
โ”œโ”€โ”€ package.json

 

โฌ‡๏ธ LoginPage.js

๋”๋ณด๊ธฐ
const { By } = require('selenium-webdriver');

class LoginPage {
  constructor(driver) {
    this.driver = driver;

    this.emailInput = By.id("email");
    this.pwdInput = By.id("password");
  };

  async getElement(el) {
    return await this.driver.findElement(el);
  }
  
  async getInputValue(locator) {
    const element = await this.driver.findElement(locator);
    return await element.getAttribute("value");
  }

  async sendTxt(el, txt) {
    await this.driver.findElement(el).sendKeys(txt);
  }

  async clickBtn(el) {
    await this.driver.findElement(el).click();
  }
}

module.exports = LoginPage;

 

โฌ‡๏ธ LoginPage.test.js

๋”๋ณด๊ธฐ
const { Builder } = require("selenium-webdriver");
const LoginPage = require('./LoginPage.js'); 

const BASE_URL = "http://localhost:3000/login";
const TIMEOUT = 10000;

describe("๋ฉ”์ธ ํŽ˜์ด์ง€ ๊ฐ์ฒด ๋ชจ๋ธ ํ…Œ์ŠคํŠธ", () => {
  let driver;
  let loginPage;

  beforeEach(async () => {
    driver = await new Builder().forBrowser("chrome").build();
    loginPage = new LoginPage(driver); 
    await driver.get(BASE_URL);
  }, TIMEOUT);

  afterEach(async () => {
    if (driver) {
      await driver.quit();
    }
  });

  it("๋กœ๊ทธ์ธ ํ…Œ์ŠคํŠธ", async () => {
    const emailValue = "asdf@naver.com";
    const pwdValue = "asdf1234";

    await loginPage.sendTxt(loginPage.emailInput, emailValue);
    await loginPage.sendTxt(loginPage.pwdInput, pwdValue);

    const actualEmailValue = await loginPage.getInputValue(loginPage.emailInput);
    const actualPwdValue = await loginPage.getInputValue(loginPage.pwdInput);

    expect(actualEmailValue).toBe(emailValue);
    expect(actualPwdValue).toBe(pwdValue);
  });
});

 

โฌ‡๏ธ package.json

๋”๋ณด๊ธฐ
{
  "dependencies": {
    "jest": "^29.7.0",
    "selenium-webdriver": "^4.31.0"
  },
  "scripts": {
    "test": "jest"
  }
}

 

JavaScript์™€ Jest์— ์ ์‘ํ•˜๊ณ ์ž ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ดค๋‹ค. ์›Œ๋‚™ ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ๋ผ ๋ฌธ์ œ ์—†์ด ์ž˜ ์ž‘๋™ํ•˜๊ธด ํ–ˆ์ง€๋งŒ, ์ฝ”๋“œ๋ฅผ ๋“ค์—ฌ๋‹ค๋ณด๋‹ˆ ๊ธˆ์„ธ ๊ฐœ์„ ํ•ด์•ผ ํ•  ๋ถ€๋ถ„๋“ค์ด ๋ˆˆ์— ๋„์—ˆ๋‹ค.

 

๐Ÿ”ฅ ๋ฌธ์ œ์  1

์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ๋Š” LoginPage ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž ๋‚ด๋ถ€์— locator๋ฅผ ์ง์ ‘ ์ •์˜ํ•œ ๊ฒƒ์ด๋‹ค. ์ฒ˜์Œ์—” ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ณด์ด์ง€๋งŒ, ์‹œ๊ฐ„์ด ์ง€๋‚ ์ˆ˜๋ก ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์ ์  ์–ด๋ ค์›Œ์ง„๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŽ˜์ด์ง€ ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์˜ ID๊ฐ€ loginBtn์—์„œ submitBtn์œผ๋กœ ๋ฐ”๋€Œ์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. locator๊ฐ€ ํด๋ž˜์Šค ๋‚ด๋ถ€์— ์ •์˜๋˜์–ด ์žˆ๋‹ค๋ฉด, ์ด ํŒŒ์ผ์„ ์—ด์–ด ์ง์ ‘ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค. ๋‹จ์ˆœํžˆ ์ˆ˜์ •๋งŒ ํ•˜๋ฉด ๋˜์ง€ ์•Š๋А๋ƒ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํŽ˜์ด์ง€๊ฐ€ ๋งŽ์•„์ง€๊ณ  ๊ณตํ†ต ์š”์†Œ๋“ค์ด ์—ฌ๋Ÿฌ ๊ณณ์— ์ค‘๋ณต๋˜๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ์ƒํ™ฉ์ด ๋‹ฌ๋ผ์ง„๋‹ค. ๊ฐ™์€ ์š”์†Œ๋ฅผ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์—์„œ ์ค‘๋ณต ์ •์˜ํ•˜๊ณ  ์žˆ์—ˆ๋‹ค๋ฉด, ๊ทธ๊ฑธ ๋ชจ๋‘ ์ฐพ์•„๊ฐ€๋ฉฐ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ๋‹ค. ๊ฒฐ๊ตญ ๋ณ€๊ฒฝ์— ์œ ์—ฐํ•˜์ง€ ๋ชปํ•œ ๊ตฌ์กฐ๊ฐ€ ๋˜๊ณ , ์ž‘์€ UI ๋ณ€๊ฒฝ์—๋„ ์ฝ”๋“œ ์ „์ฒด๋ฅผ ๋“ค์‘ค์…”์•ผ ํ•˜๋Š” ๋น„ํšจ์œจ์ด ์ƒ๊ธฐ๋Š” ๊ฒƒ์ด๋‹ค.

 

๐Ÿ”ฅ ๋ฌธ์ œ์  2

๋‘ ๋ฒˆ์งธ ๋ฌธ์ œ๋Š” ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ์˜ ํ•˜๋“œ์ฝ”๋”ฉ์ด๋‹ค.  LoginPage.test.js ํŒŒ์ผ ์•ˆ์— ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ’์„ ์ง์ ‘ ๋„ฃ์–ด๋†“๋Š” ๋ฐฉ์‹์€ ๋”ฑ 1๊ฐœ์˜ ๊ฒฝ์šฐ๋งŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์„ ๋ฟ์ด๋ฉฐ, ๋‹ค๋ฅธ ์ผ€์ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋ณต ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ์ค‘๋ณต๋„ ์ฆ๊ฐ€ํ•˜๊ณ , ๊ด€๋ฆฌ๊ฐ€ ๋ฒˆ๊ฑฐ๋กœ์›Œ์ง„๋‹ค.

 

๐Ÿ’ก ํ•ด๊ฒฐ์ฑ…

๊ทธ๋ ‡๋‹ค๋ฉด ์ด ๋ฌธ์ œ๋“ค์„ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„๊นŒ? ํ•ด๋‹ต์€ ๋ฐ”๋กœ ๋ชจ๋“ˆํ™”๋‹ค.

 

๐Ÿ“Œ ๋ณ€๊ฒฝ๋œ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ

VSCode/
โ”œโ”€โ”€ JavaScript_Prac/
โ”‚ โ”œโ”€โ”€ pages/
โ”‚ โ”‚      โ”œโ”€โ”€ LoginPage.js
โ”‚ โ”‚      โ””โ”€โ”€ LoginPage.locator.js
โ”‚ โ”œโ”€โ”€ tests/
โ”‚ โ”‚      โ”œโ”€โ”€ LoginPage.test.js
โ”‚ โ”‚      โ””โ”€โ”€ data/
โ”‚ โ”‚           โ””โ”€โ”€ loginData.js
โ”‚ โ”œโ”€โ”€ package.json

์‚ฌ์‹ค ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋Š” ๋ณธ์ธ์˜ ์ทจํ–ฅ์— ๋”ฐ๋ผ ์ž์œ ๋กญ๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด ๋œ๋‹ค. ๋งŒ์•ฝ ํšŒ์‚ฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ณต์‹ ๊ตฌ์กฐ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ฑฐ๊ธฐ์— ๋งž์ถ”๋ฉด ๋˜๋Š” ๊ฒƒ์ด๊ณ , ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ๋ณธ์ธ์ด ๊ด€๋ฆฌํ•˜๊ธฐ ํŽธํ•œ ๋ฐฉ์‹์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค. ์ด๋ฒˆ์—๋Š” ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ณธ ๊ฑฐ๋ผ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋„ ์ž„์‹œ๋กœ ๊ตฌ์„ฑํ–ˆ์ง€๋งŒ, ํŽ˜์ด์ง€ ์ˆ˜๊ฐ€ ๋งŽ์•„์ง„๋‹ค๋ฉด locators ํด๋”๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

โฌ‡๏ธ LoginPage.js

๋”๋ณด๊ธฐ
class LoginPage {
  constructor(driver) {
    this.driver = driver;
  }

  async getElement(locator) {
    return await this.driver.findElement(locator);
  }
  
  async getInputValue(locator) {
    const element = await this.driver.findElement(locator);
    return await element.getAttribute("value");
  }

  async sendTxt(locator, txt) {
    await this.driver.findElement(locator).sendKeys(txt);
  }

  async clickBtn(locator) {
    await this.driver.findElement(locator).click();
  }
}

module.exports = LoginPage;

 

โฌ‡๏ธ LoginPage.test.js

๋”๋ณด๊ธฐ
const { Builder } = require("selenium-webdriver");
const LoginPage = require("../pages/LoginPage.js");
const Locator = require("../pages/LoginPage.locator.js");
const loginData = require("./data/loginData.js");

const BASE_URL = "http://localhost:3000/login";
const TIMEOUT = 10000;

describe("๋กœ๊ทธ์ธ ํ…Œ์ŠคํŠธ (๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜)", () => {
  let driver;
  let loginPage;

  beforeEach(async () => {
    driver = await new Builder().forBrowser("chrome").build();
    loginPage = new LoginPage(driver);
    await driver.get(BASE_URL);
  }, TIMEOUT);

  afterEach(async () => {
    if (driver) {
      await driver.quit();
    }
  });

  test.each(loginData)("๋กœ๊ทธ์ธ ์‹œ ์ž…๋ ฅ๊ฐ’ ํ™•์ธ: ๋ฐ์ดํ„ฐ (%s)", async ({ email, password }) => {
    await loginPage.sendTxt(Locator.emailInput, email);
    await loginPage.sendTxt(Locator.pwdInput, password);

    const actualEmail = await loginPage.getInputValue(Locator.emailInput);
    const actualPwd = await loginPage.getInputValue(Locator.pwdInput);

    expect(actualEmail).toBe(email);
    expect(actualPwd).toBe(password);
  });
});

 

โฌ‡๏ธ LoginPage.locator.js

๋”๋ณด๊ธฐ
const { By } = require('selenium-webdriver');

const LoginPageLocator = {
  emailInput: By.id("email"),
  pwdInput: By.id("password")
};

module.exports = LoginPageLocator;

 

โฌ‡๏ธ loginData.js

๋”๋ณด๊ธฐ
module.exports = [
    { email: "asdf@naver.com", password: "asdf1234" },
    { email: "test@example.com", password: "pass1234" },
    { email: "hello@world.com", password: "securepass" }
];

 

loginData.js์— ์ด 3๊ฐœ์˜ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ •์˜ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, test.each()๋ฅผ ํ†ตํ•ด ๊ฐ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ๊ฐ€ ํ•œ ๋ฒˆ์”ฉ ์‹คํ–‰๋œ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ Tests: 3 passed๋กœ ํ‘œ์‹œ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ์ด ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ํ…Œ์ŠคํŠธ ํŒŒ์ผ(LoginPage.test.js) ๋‚ด์—์„œ ์ˆ˜ํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— Test Suites: 1 passed๋กœ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

๐Ÿ“Œ ๋ฆฌํŒฉํ† ๋ง์„ ํ†ตํ•ด ์–ป์€ ๊ตํ›ˆ

์ด์ฒ˜๋Ÿผ ํšจ์œจ์ ์ด๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ž๋™ํ™” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, ๊ธฐ๋Šฅ๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋“ˆํ™”ํ•˜๊ณ , ๊ตฌ์กฐํ™”๋œ ํŒจํ„ด(POM ๋“ฑ)์„ ๋ฐ”ํƒ•์œผ๋กœ ํ…Œ์ŠคํŠธ ์„ค๊ณ„๋ฅผ ์ฒด๊ณ„ํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค. ์ž‘์€ ๋ฆฌํŒฉํ† ๋ง์—์„œ ์‹œ์ž‘ํ•˜๋”๋ผ๋„, ๊ทธ ๊ฒฝํ—˜์€ ์‹ค์ œ ์„œ๋น„์Šค์˜ ํ’ˆ์งˆ๊ณผ ์œ ์ง€๋ณด์ˆ˜ ํšจ์œจ์„ ๋†’์ด๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋œ๋‹ค.

 

์ €์ž‘์žํ‘œ์‹œ ๋น„์˜๋ฆฌ ๋ณ€๊ฒฝ๊ธˆ์ง€

'๐Ÿ“‚ Quality Assurance Study > ๐Ÿ“„ QA ๊ณต๋ถ€' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Unity์—์„œ์˜ ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ๋ฐ Jira ์ด์Šˆ ๋“ฑ๋ก ์ž๋™ํ™”  (0) 2025.03.09
Jira REST API ํ† ํฐ ๋ฐœ๊ธ‰ ๋ฐ ์ด์Šˆ ์กฐํšŒ  (0) 2025.03.02
XPath์— ๋Œ€ํ•˜์—ฌ  (0) 2025.02.27
Jira ๋ฐ Confluence ์‚ฌ์šฉ ๋ฐฉ๋ฒ•  (2) 2025.02.20
BTS (Bug Tracking System)์— ๋Œ€ํ•˜์—ฌ  (3) 2025.01.13
'๐Ÿ“‚ Quality Assurance Study/๐Ÿ“„ QA ๊ณต๋ถ€' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • Unity์—์„œ์˜ ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ๋ฐ Jira ์ด์Šˆ ๋“ฑ๋ก ์ž๋™ํ™”
  • Jira REST API ํ† ํฐ ๋ฐœ๊ธ‰ ๋ฐ ์ด์Šˆ ์กฐํšŒ
  • XPath์— ๋Œ€ํ•˜์—ฌ
  • Jira ๋ฐ Confluence ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
YeonSu02
YeonSu02
Email : rkddustn2519@naver.com
  • YeonSu02
    IsLiife2
    YeonSu02
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ
      • ๐Ÿ“‚ Computer Science
      • ๐Ÿ“‚ Unity Engine Study
        • ๐Ÿ“„ Unity ์ธํ”„๋Ÿฐ ๊ฐ•์˜
        • ๐Ÿ“„ Unity ์œ ํŠœ๋ธŒ ๊ฐ•์˜
        • ๐Ÿ“„ Unity ์ฐธ๊ณ 
      • ๐Ÿ“‚ Game Designer Study
        • ๐Ÿ“„ ๊ธฐํš ์ธํ”„๋Ÿฐ ๊ฐ•์˜
      • ๐Ÿ“‚ Quality Assurance Study
        • ๐Ÿ”ฅ ์—˜๋ฆฌ์Šค SW QAํŠธ๋ž™
        • ๐Ÿ“„ QA ๊ณต๋ถ€
        • ๐Ÿ“š QA ์ฑ… ๋ฆฌ๋ทฐ
      • ๐Ÿ“‚ Program Language Study
        • ๐Ÿ“„ C# ๊ณต๋ถ€
        • ๐Ÿ“„ ํŒŒ์ด์ฌ ๊ณต๋ถ€
        • ๐Ÿ“„ Java ๊ณต๋ถ€
        • ๐Ÿ“„ JavaScript ๊ณต๋ถ€
      • ๐Ÿ“‚ Additional Study
        • ๐Ÿ“„ Git
        • ๐Ÿ“„ Firebase
        • ๐Ÿ“„ License
      • ๐Ÿ’ป Game Development
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
  • ๋งํฌ

    • GitHub
  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์ปดํ“จํ„ฐํ™œ์šฉ๋Šฅ๋ ฅ1๊ธ‰
    ์ปดํ™œ2๊ธ‰
    ์ •์ฒ˜๊ธฐ ํ•„๊ธฐ
    qa ์ฑ… ๋ฆฌ๋ทฐ
    ์ปดํ“จํ„ฐํ™œ์šฉ๋Šฅ๋ ฅ
    ๊ตญ์ œ์ž๊ฒฉ์ฆ
    ์ปดํ“จํ„ฐํ™œ์šฉ๋Šฅ๋ ฅ2๊ธ‰
    ์ •์ฒ˜๊ธฐ ์‹ค๊ธฐ
    ํ…Œ์ŠคํŒ…์ž๊ฒฉ์ฆ
    ์ปดํ™œ
    ์—‘์…€
    qa์ž๊ฒฉ์ฆ
    QA
    BTS
    ์ •์ฒ˜๊ธฐ ๋…ํ•™
    ์ž๊ฒฉ์ฆ
    ISTQB
    istqb-ctfl
    qa ์ฑ…
    ์ •์ฒ˜๊ธฐ
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
YeonSu02
[Jest + POM] ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•œ ํ…Œ์ŠคํŠธ ๋ฆฌํŒฉํ† ๋ง
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”