BDD 시나리오가 100개쯤 쌓였다
리팩토링하면서 보니까 잘못된 패턴이 반복해서 나타났다
매번 "이건 아닌데" 하면서 고쳤던 것들을 정리한다
패턴 1. When 에 검증이 들어간다
# Bad
When 분자 업로드가 완료되었다
When 결과가 화면에 표시된다
When 프로젝트 생성이 성공한다
When 은 액션 만 써야 한다
"완료되었다", "표시된다", "성공한다" 는 검증이다
검증은 Then 에 써야 하는데, 쓰다 보면 When 에 슬쩍 들어간다
# Good
When SDF 파일을 업로드한다
Then 분자가 목록에 표시된다
When 에 검증이 들어가면 두 가지 문제가 생긴다
첫째, Step 함수 안에서 assert 와 click() 이 같이 있게 된다
둘째, 이 Step 이 통과하면 "업로드가 됐다는 건지, 업로드를 했다는 건지" 구분이 안 된다
패턴 2. Then 에 클릭이 들어간다
# Bad
Then 저장 버튼을 클릭한다
Then 확인 모달에서 확인을 누른다
Then 다음 단계로 이동한다
Then 은 검증 만 써야 한다
저장 버튼 클릭은 When 이다
# Good
When 저장 버튼을 클릭한다
Then 저장 완료 메시지가 표시된다
Then 에 클릭이 들어가면 실패했을 때 뭘 확인해야 하는지 모른다
"저장 버튼 클릭이 실패한 건지, 저장이 안 된 건지"
패턴 3. Given 이 시나리오가 된다
# Bad
Given 로그인한다
Given 프로젝트를 생성한다
Given 벤치를 생성한다
Given SDF 파일을 업로드한다
Given 시뮬레이션을 실행한다
When 결과 탭을 클릭한다
Then 바인딩 스코어가 표시된다
Given 이 5줄이다
Given 은 사전 조건 (상태) 을 설정하는 것이다
"~한 상태이다" 로 읽혀야 하는데, 위 예시는 그냥 시나리오 흐름이다
# Good
Given 시뮬레이션 결과가 존재한다 ← 상태
When 결과 탭을 클릭한다
Then 바인딩 스코어가 표시된다
"시뮬레이션 결과가 존재한다" 는 상태다
어떻게 그 상태를 만드는지는 Step 함수가 처리하면 된다
Given 이 길어지면 시나리오를 읽었을 때 "이 테스트가 뭘 검증하는지"
파악하는 데 시간이 걸린다
Given 이 2줄을 넘기면 한 번 의심해 보는 게 좋다
패턴 4. Step 함수에 로직을 직접 쓴다
# Bad - Step 안에서 직접 page 조작
@when("SDF 파일을 선택하여 업로드한다")
def upload_sdf(bench_page, bench_context):
file_input = bench_page.driver.page.locator('input[type="file"]')
if file_input.count() > 0:
file_input.first.set_input_files(file_path)
else:
raise Exception("File input not found")
next_btn = bench_page.driver.page.get_by_role("button", name="다음")
if next_btn.count() > 0:
next_btn.last.click()
# ... 계속 이어짐
이 Step 함수가 70줄이었다
Step 은 Page Object 메서드 호출만 해야 한다
# Good - Step 은 POM 메서드를 호출만 한다
@when("SDF 파일을 선택하여 업로드한다")
def upload_sdf(bench_page, bench_context):
bench_page.upload_sdf_file(bench_context["sdf_file_path"])
Step 에 로직이 있으면 두 가지 문제가 생긴다
첫째, 같은 업로드 로직이 필요한 다른 시나리오에서 재사용이 안 된다
둘째, UI 가 바뀌면 이 Step 을 직접 수정해야 한다
로케이터는 locators/ 에, 동작은 *_page.py 에, Step 은 호출만
이 규칙 하나가 리팩토링 시간을 가장 많이 줄여줬다
패턴 5. 한글 조사 차이로 Step 이 중복 등록된다
# 어딘가에 이미 존재
@given("프로젝트가 존재한다")
def project_exists(shared_project_context):
...
# 다른 파일에 실수로 추가
@given("프로젝트이 존재한다") ← '이' vs '가'
def project_exists_2(shared_project_context):
...
"이" 와 "가" 차이다
pytest-bdd 는 텍스트가 정확히 일치해야 Step 을 찾는다
"프로젝트가" 와 "프로젝트이" 는 다른 Step 으로 인식한다
.feature 파일에서 프로젝트이 를 쓰면 프로젝트가 Step 이 매핑되지 않는다
ERRORS
E pytest_bdd.exceptions.StepDefinitionNotFoundError:
Step definition is not found: Given "프로젝트이 존재한다"
같은 내용의 Step 이 두 가지 형태로 등록되는 것도 문제다
# 둘 다 존재하는 경우
@given("프로젝트를 생성한다")
@given("프로젝트가 생성된다")
# feature 파일마다 다르게 쓰임
# scenario_a.feature: Given 프로젝트를 생성한다
# scenario_b.feature: Given 프로젝트가 생성된다
Step 텍스트를 한 곳에 상수로 모아두면 해결된다
# tests/step_definitions/step_texts.py
class StepTexts:
PROJECT_EXISTS = "프로젝트가 존재한다"
PROJECT_CREATED = "프로젝트를 생성한다"
아니면 최소한 프로젝트 내에서 조사 규칙을 하나로 통일하면 된다
이/가 중 하나, 을/를 중 하나, 일관되게
정리
| 패턴 | 잘못된 것 | 고치는 방법 |
| When 에 검증 | "완료되었다", "표시된다" | Then 으로 이동 |
| Then 에 클릭 | "버튼을 클릭한다" | When 으로 이동 |
| Given 이 Flow | Given 5줄 | 상태 단어로 압축 |
| Step 에 로직 | page.locator() 직접 호출 | POM 메서드로 추출 |
| 조사 불일치 | "이" vs "가" Step 중복 | Step 텍스트 상수화 |
처음에는 Given/ When/ Then 이 대충 구분되면 된다고 생각했다
리팩토링하다 보니까 이 구분이 명확하지 않으면 Step 재사용이 안 되고, 실패 원인 파악이 어려워지고, Step 중복 등록 에러가 난다
BDD 는 규칙이 아니라 테스트를 읽기 쉽게 만들기 위한 수단 이다
Given = 상태, When = 액션, Then = 검증 이 세 줄만 지키면 나머지는 따라온다
'TIL > Python' 카테고리의 다른 글
| [TIL][GitHub Actions] Confluence 변경 자동 감지 및 문서 업데이트 시스템 (0) | 2026.03.10 |
|---|---|
| [TIL] pathlib 로 디렉토리 내 파일 총합 구하기 w/ Python (1) | 2025.07.09 |
| input = sys.stdin.readline() 과 input = sys.stdin.readline 의 차이 (1) | 2025.05.23 |
| Insert Dummy Data into MySQL w/ Python (0) | 2025.02.11 |
| [Python] split() 와 strip() (0) | 2024.12.03 |