TIL/Python

[TIL][pytest-bdd] Given/ When/ Then 잘못 쓰는 패턴 5가지

아람2 2026. 3. 2. 18:26
반응형

BDD 시나리오가 100개쯤 쌓였다 

리팩토링하면서 보니까 잘못된 패턴이 반복해서 나타났다 

매번 "이건 아닌데" 하면서 고쳤던 것들을 정리한다 

패턴 1. When 에 검증이 들어간다 

# Bad
When 분자 업로드가 완료되었다
When 결과가 화면에 표시된다
When 프로젝트 생성이 성공한다

When 은 액션 만 써야 한다

"완료되었다", "표시된다", "성공한다" 는 검증이다

검증은 Then 에 써야 하는데, 쓰다 보면 When 에 슬쩍 들어간다

# Good
When SDF 파일을 업로드한다
Then 분자가 목록에 표시된다

When 에 검증이 들어가면 두 가지 문제가 생긴다

첫째, Step 함수 안에서 assertclick() 이 같이 있게 된다

둘째, 이 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 = 검증 이 세 줄만 지키면 나머지는 따라온다

반응형