【Selenium×Python】로그인 폼 유효성 검사 테스트 35개 자동화 방법

테스트 자동화

📌 이런 분께 추천합니다

  • Selenium으로 웹 폼 테스트를 자동화하고 싶은 분
  • 로그인 폼의 유효성 검사를 빠짐없이 확인하고 싶은 분
  • 경계값·특수문자·SQL 인젝션 등의 테스트 케이스 설계를 배우고 싶은 분
  • Python으로 E2E 테스트를 구현해보고 싶은 QA 엔지니어

✅ 이 글을 읽으면 알 수 있는 것

  • Selenium으로 35개의 폼 유효성 검사 테스트를 자동 실행하는 방법
  • 전각·반각·경계값·특수문자 테스트 케이스 설계 패턴
  • FAIL 케이스의 스크린샷 자동 저장 구현 방법
  • 테스트 결과를 CSV·JSON 리포트로 출력하는 방법

👨‍💻 작성자 소개

QA 엔지니어로서 실무에서 Selenium을 활용한 자동화 테스트를 담당하고 있습니다. 본 글에서 소개하는 스크립트는 GitHub에 공개되어 있으며, 실제로 동작 확인이 완료된 코드를 그대로 해설합니다.

로그인 폼은 대부분의 웹 애플리케이션에서 가장 중요한 기능 중 하나입니다. 유효성 검사가 제대로 동작하지 않으면 사용자가 로그인할 수 없는 것은 물론, SQL 인젝션이나 무단 접근 같은 심각한 보안 문제로 이어질 수 있습니다. 그렇기 때문에 릴리스 전에 빠짐없는 테스트를 자동화해두는 것이 매우 중요합니다.

로그인 폼을 수동으로 테스트하면 생각보다 꽤 번거롭지 않으신가요? 전각 문자·빈 문자열·SQL 인젝션·이모지까지, 확인해야 할 항목은 끝이 없습니다. 이 글에서는 Selenium을 사용해 로그인 폼 유효성 검사 테스트를 35개 자동 실행하는 Python 스크립트를 해설합니다. 결과는 CSV·JSON으로 출력되고, FAIL 케이스는 스크린샷도 자동 저장됩니다.


테스트 관점

이번 스크립트에서는 로그인 폼에 대해 아래의 관점을 빠짐없이 테스트합니다. 실무 QA 현장에서도 자주 사용되는 정석 관점들이므로, 그대로 자사 제품의 테스트 설계에도 응용할 수 있습니다.

  • 빈 입력 체크:사용자 이름·비밀번호가 미입력일 때 오류가 반환되는가
  • 문자 종류 체크:전각·일본어·이모지 등 예상 외의 문자 종류를 거부하는가
  • 형식·공백 혼입 체크:형식이 잘못된 입력(이메일 형식, 공백 포함)을 올바르게 거부하는가
  • 경계값 체크:1자·255자 등 길이의 끝점에서 이상이 발생하지 않는가
  • 보안 체크:SQL 인젝션·XSS 등의 공격 문자열을 무해화하는가
  • 정상 로그인:올바른 인증 정보로 로그인에 성공하고 리다이렉트되는가

테스트 케이스 일람(전체 35개)

테스트 케이스는 4개의 카테고리로 구성됩니다. 실무에서 놓치기 쉬운 전각 문자나 특수문자 계열을 두텁게 커버하고 있습니다.

카테고리ID건수주요 내용
전각 문자Z-01〜Z-088건전각 영자·일본어·히라가나·카타카나·전각 기호·전각 공백
반각 문자H-01〜H-077건대문자·소문자·숫자만·공백 혼입·이메일 형식
길이 / 경계값L-01〜L-1010건빈 문자열·1자·2자·3자·50자·255자
특수 문자S-01〜S-1010건SQL 인젝션·XSS·이모지·개행·탭·URL 인코딩

⚠️ 숨겨진 장치:H-01(정상 로그인)은 expected_error: True로 의도적으로 잘못된 기댓값을 설정했습니다. 이는 데모용으로 FAIL을 발생시켜 스크린샷 저장 동작을 확인하기 위한 장치입니다. 실제 사용 시에는 False로 수정하세요.

셋업 및 실행 방법

필요한 라이브러리 설치

아래 두 가지 라이브러리만 설치하면 동작합니다. ChromeDriver 수동 관리는 필요 없습니다.

pip install selenium webdriver-manager

실행 명령어

# 브라우저를 표시하며 실행(기본값)
python form_validation_test.py

# 헤드리스 모드(브라우저 비표시·CI 환경용)
python form_validation_test.py --headless

💡 Tip:첫 실행 시 webdriver-manager가 ChromeDriver를 자동으로 다운로드합니다. 두 번째 실행부터는 캐시를 사용하므로 빠르게 시작됩니다.

실행 결과 샘플

실행하면 터미널에 실시간으로 결과가 출력됩니다. FAIL 케이스는 원인과 함께 스크린샷 경로도 표시됩니다.

==============================================================
  Selenium Form Validation Test (headless)
  Target : //the-internet.herokuapp.com/login
  Cases  : 35
  Start  : 2025-07-01 10:00:00
==============================================================
  (01/35) Full-width alpha username      ... ✅ PASS (1823ms)
  (02/35) Full-width numeric password    ... ✅ PASS (1654ms)
  (03/35) Full-width space in username   ... ✅ PASS (1701ms)
  ...
  (09/35) Valid login (correct creds)    ... ❌ FAIL (2100ms)
       → expected_error:True / actual_error:False
       📸 screenshots/H-01_FAIL.png
  ...
  (35/35) URL-encoded characters         ... ✅ PASS (1590ms)

──────────────────────────────────────────────────────────────
  Test Summary
──────────────────────────────────────────────────────────────
  Total  : 35
  ✅ PASS  : 34
  ❌ FAIL  : 1
  ⚠️  ERROR : 0
  Rate   : 97.1%
──────────────────────────────────────────────────────────────

코드 해설:핵심 포인트 3가지

① JavaScript를 통해 폼에 값 입력하기

Selenium의 send_keys()는 개행(\n)이나 탭(\t)같은 특수문자를 정확히 입력하지 못하는 경우가 있습니다. JavaScript로 직접 값을 설정하면 어떤 문자든 안전하게 입력할 수 있습니다.

arguments[0]에는 WebElement, arguments[1]에는 입력할 문자열을 전달합니다. 이를 통해 브라우저의 키 이벤트 처리를 건너뛰고 DOM 요소에 직접 값을 씁니다. 이모지·전각 문자·제어 문자처럼 send_keys로는 깨지기 쉬운 문자도 확실하게 동작하므로, 특수문자 계열 테스트 케이스(S-05 개행·S-06 탭)는 이 방식 덕분에 성립합니다.

driver.execute_script("arguments[0].value = arguments[1];", u_elem, tc["username"])
driver.execute_script("arguments[0].value = arguments[1];", p_elem, tc["password"])

② HTML5 유효성 검사 우회하기

빈 문자열 테스트(L-01〜L-03)는 HTML5의 required 속성 때문에 브라우저 측에서 차단됩니다. novalidate를 부여하면 요청이 서버까지 도달하므로 서버 측 동작까지 확인할 수 있습니다.

유효성 검사 테스트의 본래 목적은 “브라우저가 오류를 표시하는가”가 아니라 “서버가 올바르게 오류를 반환하는가”입니다. 프론트엔드 유효성 검사를 의도적으로 무효화함으로써 백엔드의 견고성을 직접 테스트할 수 있습니다.

driver.execute_script(
    "document.querySelector('form').setAttribute('novalidate','true');"
)

③ FAIL 케이스만 스크린샷 저장하기

모든 케이스의 스크린샷을 찍으면 파일 수가 방대해집니다. FAIL 시에만 저장하는 설계로 나중에 원인 확인이 훨씬 쉬워집니다.

파일명은 {테스트ID}_FAIL.png 형식이므로 파일명만 봐도 어느 테스트 케이스가 실패했는지 바로 알 수 있습니다. GitHub Actions 같은 CI/CD 환경에서 테스트를 실행할 때도 이 스크린샷을 아티팩트로 저장해두면 디버깅 효율이 크게 향상됩니다.

if r.status == "FAIL":
    os.makedirs("screenshots", exist_ok=True)
    ss = f"screenshots/{tc['id']}_FAIL.png"
    driver.save_screenshot(ss)
    r.screenshot = ss

💡 실무 Tip:Page Object Model(POM)에 대해
실무에서는 Page Object Model(POM)을 사용해 테스트 코드와 페이지 조작을 분리하는 설계가 권장됩니다. 이 스크립트는 간단한 구성이지만, 테스트 케이스가 늘어나면 LoginPage 클래스에 폼 조작을 모아두는 것으로 유지보수성과 재사용성이 크게 향상됩니다.

리포트를 CSV·JSON으로 자동 출력하기

테스트 완료 후 타임스탬프가 붙은 리포트 파일이 자동으로 생성됩니다.

📄 report_20250701_100000.csv
📄 report_20250701_100000.json
출력 형식주요 용도
CSVExcel에서 열어 리뷰·필터링·팀 공유·에비던스 제출
JSONGitHub Actions 연동·Slack 알림·Allure 리포트 데이터 연계

더 발전시키는 아이디어

⚡ 병렬 실행으로 고속화

concurrent.futures를 사용해 브라우저를 여러 개 기동하고 테스트를 병렬 실행하면 실행 시간을 대폭 단축할 수 있습니다.

🔄 재시도 처리 추가

네트워크가 불안정한 환경에서도 안정적으로 동작하도록 ERROR 케이스에 자동 재시도 로직을 추가할 수 있습니다.

📊 리포트 시각화

JSON 리포트를 바탕으로 Allure나 HTML 리포트를 생성하면 팀과의 공유가 훨씬 원활해집니다.

🤖 CI/CD 연동

GitHub Actions나 Jenkins에 통합해 PR마다 자동으로 테스트를 실행하는 구조를 만들 수 있습니다.

💡 활용 시나리오 및 확장 예시

  • 매 릴리스 전 회귀 테스트의 일환으로 실행
  • 신기능 릴리스 전 로그인 기능 정상 동작 확인
  • 스테이징 환경에서의 보안 확인(SQLi·XSS)
  • 회원가입·비밀번호 변경 폼으로 응용 확장
  • 테스트 케이스를 CSV에서 읽어오는 방식으로 변경해 노코드 관리
  • pytest-html이나 Allure와 조합해 리포트 시각화

09. 자주 겪는 문제 & 해결법

구현 중 실제로 겪었던 문제들을 정리했습니다. 같은 곳에서 막히는 분들께 도움이 되면 좋겠습니다.


① send_keys() 로 특수 문자가 올바르게 입력되지 않는다

줄바꿈(\n)·탭(\t)·이모지 등의 특수 문자를 send_keys() 로 입력하면 문자가 깨지거나 의도치 않은 동작이 발생하는 경우가 있었습니다. 특수 문자는 JavaScript를 통해 DOM에 직접 값을 쓰는 방식으로 해결했습니다.

# ❌ send_keys()는 특수 문자가 깨질 수 있음
username_field.send_keys("test\nuser")

# ✅ JS를 통해 직접 값을 설정
driver.execute_script("arguments[0].value = arguments[1];", username_field, "test\nuser")

💡 포인트: arguments[0] 에 WebElement, arguments[1] 에 입력할 문자열을 전달하면 브라우저의 키 이벤트 처리를 건너뛰고 확실하게 값을 쓸 수 있습니다.


② HTML5의 required 속성으로 브라우저에서 차단된다

빈 문자열 테스트를 실행하려 했더니 폼의 required 속성으로 인해 브라우저 측에서 요청이 차단되어 서버 측 동작을 확인할 수 없었습니다.

# ❌ required가 있으면 브라우저가 차단하여 서버에 전달되지 않음
submit_button.click()

# ✅ novalidate를 추가하여 HTML5 유효성 검사를 우회
driver.execute_script(
    "document.querySelector('form').setAttribute('novalidate', 'true');"
)
submit_button.click()

💡 포인트: 「브라우저가 오류를 표시하지 않는다=테스트 통과」가 아니라, 「서버가 올바르게 오류를 반환하는가」까지 확인하는 것이 유효성 검사 테스트의 본래 목적입니다.


③ 전체 스크린샷으로 파일 수가 방대해졌다

처음에는 전체 35건에 대해 매번 스크린샷을 찍었더니 파일이 대량으로 생성되어 어떤 것이 중요한지 알 수 없게 됐습니다. FAIL 케이스만 저장하는 설계로 바꾸니 관리하기 쉬워졌습니다.

# ❌ 전체 저장하면 파일 수가 방대해짐
driver.save_screenshot(f"screenshots/{tc['id']}.png")

# ✅ FAIL일 때만 저장
if result.status == "FAIL":
    os.makedirs("screenshots", exist_ok=True)
    driver.save_screenshot(f"screenshots/{tc['id']}_FAIL.png")

💡 포인트: 파일명을 {테스트ID}_FAIL.png 형식으로 해두면 어떤 테스트 케이스에서 실패했는지 파일명만 봐도 바로 알 수 있습니다.


④ 기댓값 설정 실수로 FAIL이 계속 나왔다

정상 로그인 테스트 케이스(H-01)에 expected_error: True 로 잘못 설정해서, 올바르게 로그인되고 있는데도 계속 FAIL로 판정되었습니다. 기댓값 설정 실수는 디버깅 시 알아채기 어려우므로 주의가 필요합니다.

# ❌ 기댓값이 잘못되면 정상 동작에서도 FAIL이 됨
{"id": "H-01", "username": "tomsmith", "password": "SuperSecretPassword!", "expected_error": True}

# ✅ 정상 로그인은 expected_error: False가 맞음
{"id": "H-01", "username": "tomsmith", "password": "SuperSecretPassword!", "expected_error": False}

⚠️ 주의: 테스트가 FAIL했을 때, 먼저 「코드 버그인지」「기댓값 설정 실수인지」를 구분하는 것이 중요합니다.


⑤ 오류 메시지 취득 타이밍이 너무 빨랐다

폼 제출 직후에 오류 메시지를 취득하려 했더니 아직 화면이 업데이트되지 않아 오류 메시지를 찾지 못하는 경우가 있었습니다. 제출 후 대기 처리가 필요했습니다.

# ❌ 제출 직후에 오류를 취득하려 하면 찾지 못할 수 있음
submit_button.click()
error = driver.find_element(By.ID, "flash")

# ✅ WebDriverWait으로 요소가 표시될 때까지 대기
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

submit_button.click()
error = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "flash"))
)

💡 포인트: time.sleep() 으로 고정 대기하는 것보다 WebDriverWait 으로 조건 대기하는 편이 더 빠르고 안정적입니다.

정리

이 글에서는 Selenium과 Python으로 로그인 폼 유효성 검사 테스트를 자동화하는 방법을 소개했습니다.

포인트내용
테스트 케이스전각·반각·경계값·특수문자 4가지 카테고리 총 35건
입력 방법JS를 통해 어떤 특수문자도 확실하고 안전하게 입력
스크린샷FAIL 케이스만 자동 저장(건수를 줄여 관리 용이)
리포트 출력CSV·JSON 형식으로 타임스탬프 포함 자동 생성
실행 모드--headless 옵션으로 CI/CD 환경에도 대응

실무에서는 이러한 테스트를 CI/CD 파이프라인에 통합하고 풀 리퀘스트마다 자동 실행함으로써 품질을 지속적으로 검증합니다. Selenium 테스트는 회귀 테스트에도 널리 활용되며, 배포마다 로그인 기능이 정상적으로 동작하는지 자동으로 확인할 수 있습니다.

소스 코드는 GitHub에 공개되어 있으니 꼭 직접 실행해보세요!폼의 종류를 바꾸는 것만으로 다양한 웹 애플리케이션의 테스트에 응용할 수 있습니다. 궁금한 점은 댓글로 남겨주세요 👇

제목과 URL을 복사했습니다