전자상거래 플랫폼 UI 자동화 테스트 프레임워크 구축

프로젝트 개요

본 문서는 다목적 전자상거래 사이트인 '智选优品'의 웹 기반 UI 자동화 테스트를 위한 시스템 구현 사례를 설명합니다. 주요 핵심 카테고리로는 디지털 오피스, 패션 의류, 명품 백 등이 포함되며, 사용자 인터페이스 검증을 중심으로 로그인, 상품 탐색, 장바구니 조작, 주문 처리 등의 기능을 포괄적으로 테스트합니다.

테스트 목표 및 범위

  • 각 페이지 구성 요소의 정상적인 로딩 및 상호작용 확인
  • 사용자 인증(로그인/회원가입) 및 프로필 관리 기능 검증
  • 상품 검색, 세부 정보 조회, 필터링 기능의 정확성 평가
  • 장바구니 항목 추가/수정/삭제 동작의 예상 결과 일치 여부 확인
  • 주문 생성 및 주소 관리 기능 검증
  • CSV 기반 데이터 드리븐 테스트, 실행 로그 기록, 오류 시 자동 스크린샷 캡처 기능 구현

테스트 아키텍처 구성

모듈 클래스 주요 기능
사용자 모듈 LoginPage / RegistrationPage / UserProfilePage 로그인 성공/실패, 회원가입, 프로필 수정
상품 모듈 MainPage / ProductDetailPage / SearchResultsPage 홈 화면 상품 노출, 검색 기능, 상세 페이지 접근
장바구니 모듈 CartManagementPage 항목 추가, 수량 변경, 삭제, 관심 상품 이동
주문 모듈 OrderConfigurationPage 배송지 관리, 주문 내역 확인
공통 유틸리티 DataLoader / DriverFactory / Constants CSV 데이터 파싱, WebDriver 초기화, URL 상수 관리

디렉터리 구조

ecommerce-test-suite/
├── src/main/java
│   ├── core
│   │   ├── DataLoader.java          // CSV 데이터 읽기
│   │   ├── DriverFactory.java       // 브라우저 드라이버 관리
│   │   └── Constants.java           // 시스템 상수 정의
│   ├── pages
│   │   ├── LoginPage.java
│   │   ├── RegistrationPage.java
│   │   ├── ProductDetailPage.java
│   │   ├── CartManagementPage.java
│   │   └── OrderConfigurationPage.java
│   └── utils
│       └── ScreenshotManager.java   // 스크린샷 저장
├── resources
│   ├── testdata.csv                 // 테스트 입력 데이터
│   └── logback.xml                  // 로깅 설정
└── images/                          // 실패 시 캡처된 이미지 저장

핵심 코드 구현

1. 공통 유틸리티 클래스

DataLoader.java
import java.io.*;
import java.util.*;

public class DataLoader {
    public static List<Map<String, String>> loadTestData(String path) throws Exception {
        List<Map<String, String>> records = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            String headerLine = reader.readLine();
            if (headerLine == null) return records;
            
            String[] headers = headerLine.split(",");
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.isEmpty()) continue;

                String[] values = line.split(",");
                Map<String, String> row = new HashMap<>();
                for (int i = 0; i < headers.length; i++) {
                    String value = i < values.length ? values[i] : "";
                    row.put(headers[i], value);
                }
                records.add(row);
            }
        }
        return records;
    }
}
Constants.java
public class Constants {
    public static final String BASE_URL = "http://49.235.61.184";
    public static final String LOGIN_PAGE = BASE_URL + "/?s=user/loginInfo.html";
    public static final String REGISTRATION_PAGE = BASE_URL + "/?s=user/reginfo.html";
    public static final String PRODUCT_DETAIL = BASE_URL + "/?s=goods/index/id/32.html";
    public static final String CART_PAGE = BASE_URL + "/?s=cart/index.html";
    public static final String ORDER_HISTORY = BASE_URL + "/?s=buy/index.html";
    public static final String PROFILE_CENTER = BASE_URL + "/?s=user/index.html";
}
DriverFactory.java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.*;
import java.time.Duration;

public class DriverFactory {
    private static WebDriver driver;

    public static WebDriver getDriver(String url) {
        if (driver == null) {
            ChromeOptions options = new ChromeOptions();
            options.addArguments("--remote-allow-origins=*");
            WebDriverManager.chromedriver().setup();
            driver = new ChromeDriver(options);
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
        }
        driver.get(url);
        return driver;
    }

    public static void quitDriver() {
        if (driver != null) {
            driver.quit();
            driver = null;
        }
    }
}

2. 페이지 객체 모델 (POM)

LoginPage.java
import org.openqa.selenium.support.*;
import org.openqa.selenium.*;

public class LoginPage {
    private WebDriver driver;

    public LoginPage() {
        this.driver = DriverFactory.getDriver(Constants.LOGIN_PAGE);
        PageFactory.initElements(driver, this);
    }

    @FindBy(css = "input[name='accounts']")
    private WebElement usernameField;

    @FindBy(css = "input[name='pwd']")
    private WebElement passwordField;

    @FindBy(css = "input[name='verify']")
    private WebElement captchaField;

    @FindBy(css = "button.btn-loading-example")
    private WebElement loginButton;

    @FindBy(css = "em.user")
    private WebElement userIndicator;

    public void attemptLogin(String username, String password) throws InterruptedException {
        usernameField.clear();
        passwordField.clear();
        captchaField.clear();

        usernameField.sendKeys(username);
        passwordField.sendKeys(password);

        WebElement captchaImage = driver.findElement(By.id("form-verify-img"));
        String recognizedCode = recognizeCaptcha(captchaImage);
        captchaField.sendKeys(recognizedCode);

        loginButton.click();
        Thread.sleep(500);
    }

    private String recognizeCaptcha(WebElement element) {
        // Tesseract OCR 기반 캡차 인식 로직 (생략)
        return "1234";
    }

    public boolean isLoggedIn() {
        try {
            return userIndicator.isDisplayed();
        } catch (NoSuchElementException e) {
            return false;
        }
    }
}
ProductDetailPage.java
public class ProductDetailPage {
    private WebDriver driver;

    public ProductDetailPage() {
        this.driver = DriverFactory.getDriver(Constants.PRODUCT_DETAIL);
        PageFactory.initElements(driver, this);
    }

    @FindBy(css = "em.goods-favor-text")
    private WebElement favoriteButton;

    @FindBy(css = "i.am-icon-opencart")
    private WebElement addToCartButton;

    @FindBy(css = "button.buy-submit")
    private WebElement buyNowButton;

    @FindBy(css = "input.operate-number-input")
    private WebElement quantityInput;

    public void addToFavorites() {
        favoriteButton.click();
    }

    public void addItemToCart(int count) {
        quantityInput.clear();
        quantityInput.sendKeys(String.valueOf(count));
        addToCartButton.click();
    }

    public void proceedToCheckout() {
        buyNowButton.click();
    }
}
CartManagementPage.java
public class CartManagementPage {
    private WebDriver driver;

    public CartManagementPage() {
        this.driver = DriverFactory.getDriver(Constants.CART_PAGE);
        PageFactory.initElements(driver, this);
    }

    @FindBy(css = "input[type='number']")
    private List<WebElement> quantityInputs;

    @FindBy(css = "a.submit-ajax")
    private WebElement moveToWishlistLink;

    @FindBy(css = "a.submit-delete")
    private WebElement removeItemLink;

    @FindBy(css = "button[type='submit']")
    private WebElement checkoutButton;

    public void updateItemQuantity(int index, int newValue) {
        WebElement input = quantityInputs.get(index);
        input.clear();
        input.sendKeys(String.valueOf(newValue));
    }

    public void moveToWishlist() {
        moveToWishlistLink.click();
    }

    public void removeItem() {
        removeItemLink.click();
    }

    public void goToPayment() {
        checkoutButton.click();
    }
}

테스트 실행 결과 요약

테스트 영역 총 테스트 수 성공 실패 건너뜀 성공률
사용자 인증 및 프로필 15 15 0 0 100%
상품 탐색 및 검색 9 9 0 0 100%
장바구니 조작 5 5 0 0 100%
주문 및 배송지 관리 5 5 0 0 100%
전체 합계 34 34 0 0 100%

발견된 이슈 및 리스크 분석

  • 현재 결함 없음: 모든 테스트 케이스가 성공하여 프론트엔드 기능에 대한 주요 오류는 발견되지 않음
  • 유지보수 리스크: CSS/XPath 선택자가 변경될 경우 테스트 스크립트가 실패할 수 있음
  • 타이밍 문제: 네트워크 지연 또는 서버 반응 속도에 따라 가끔 발생하는 타임아웃 현상
  • 자동화 한계: UI 기반 테스트는 API 수준보다 유지비용이 높으며, UI 재설계 시 대규모 수정 필요
  • 데이터 동기화: CSV 테스트 데이터와 실제 시스템 상태 간 불일치 가능성 존재

최종 평가

  1. 전자상거래 시스템의 사용자 인증, 상품 관리, 장바구니, 주문 처리 핵심 기능들이 요구사항과 일치하며 정상 작동함이 입증됨.
  2. POM 기반 자동화 프레임워크는 모듈화가 잘 이루어져 있어 가독성과 유지보수성이 우수함.
  3. 데이터 기반 테스트, 오류 스크린샷, 실행 로그 기능이 안정적으로 작동하며 문제 추적이 가능함.
  4. 모든 테스트 케이스 통과로 인해 시스템은 출시 기준을 충족한다고 판단됨.

태그: selenium PageObjectModel DataDrivenTesting WebDriverManager TesseractOCR

7월 31일 18:56에 게시됨