프로젝트 개요
본 문서는 다목적 전자상거래 사이트인 '智选优品'의 웹 기반 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 테스트 데이터와 실제 시스템 상태 간 불일치 가능성 존재
최종 평가
- 전자상거래 시스템의 사용자 인증, 상품 관리, 장바구니, 주문 처리 핵심 기능들이 요구사항과 일치하며 정상 작동함이 입증됨.
- POM 기반 자동화 프레임워크는 모듈화가 잘 이루어져 있어 가독성과 유지보수성이 우수함.
- 데이터 기반 테스트, 오류 스크린샷, 실행 로그 기능이 안정적으로 작동하며 문제 추적이 가능함.
- 모든 테스트 케이스 통과로 인해 시스템은 출시 기준을 충족한다고 판단됨.