Automated Test

Selenium과 Pytest를 활용한 자동화 테스트

stdout 2024. 8. 7. 09:11

가장 유명한 웹 브라우저 테스트 도구라고 할 수 있는 Selenium은 QA로서 자동화 테스트를 구현하고자 한다면

Appium, Playwright와 더불어서 필수적으로 익히게 될 도구 중 하나라고 생각합니다.

 

저는 소프트웨어 QA를 시작할때 여러 언어 중 Python을 선택했고, 제가 Python에서 사용하려고 마음먹은 라이브러리는 'Pytest' 입니다.

파이썬에 내장된 Unittest와 비교했을때 기능이든 생태계(커뮤니티 등)든 편의성이든 모두 Pytest가 앞선다고 생각했기 때문입니다.

 

그리하여 저는 Selenium과 Pytest를 함께 사용하여 웹 자동화 테스팅을 진행하는게 1차적인 목표였고, 그 두 가지 모두 인터넷 상 워낙 많은 자료가 있는 툴들이라 기초적인 사용법은 빠르게 습득할 수 있었습니다.

 

당시 별 프로젝트를 진행하지 않고 있던 상황이라, 어느 페이지로 연습할까 찾아보다가 알게된 사이트가 있습니다.

 

 

Web Testing Page

Test your automation framework here.

seleniumbase.io

 

seleniumbase라는 툴(selenium 기반으로 만들어짐)을 테스트하는 용도로 만들어진 것으로 보이는데, selenium에서도 충분히 상호작용이 가능할 것으로 보여 해당 사이트에서 테스트를 진행했습니다.

 

해당 사이트에 구현된 상호작용들로 자동화 테스트를 구현한 Python 코드는 다음과 같습니다.

# ====== 브라우저 세팅 ======
@pytest.fixture(scope="session", params=["Edge", "Chrome", "Firefox"])
def browser(request):
    if request.param == "Edge":
        driver = webdriver.Edge(options=options, service = s)
        driver.get(URL)
        driver.maximize_window()
        time.sleep(3)
    elif request.param == "Chrome":
        driver = webdriver.Chrome()
        driver.get(URL)
        driver.maximize_window()
        time.sleep(3)
    elif request.param == "Firefox":
        driver = webdriver.Firefox()
        driver.get(URL)
        driver.maximize_window()
        time.sleep(3)
    yield driver

#1. 접속한 URL이 설정한 URL과 일치하는지 확인
def test_URL(browser):
    time.sleep(5)
    assert browser.current_url == URL

#2. 초록색 버튼을 클릭했을때 보라색 버튼으로 정상적으로 변경되는지 확인
def test_color_green(browser):
    colorstring = browser.find_element(By.XPATH,'//*[@id="myButton"]').get_attribute('style')
    if colorstring == 'color: green;':
        ColorButton = browser.find_element(By.XPATH,'//*[@id="myButton"]')
        browser.execute_script("arguments[0].click();", ColorButton)
        assert browser.find_element(By.XPATH,'//*[@id="myButton"]').get_attribute('style') == 'color: purple;'

#3. 반대로 보라색 버튼을 클릭했을때 초록색 버튼으로 정상적으로 변경되는지 확인
def test_color_purple(browser):
    colorstring = browser.find_element(By.XPATH,'//*[@id="myButton"]').get_attribute('style')
    if colorstring == 'color: purple;':
        ColorButton = browser.find_element(By.XPATH,'//*[@id="myButton"]')
        browser.execute_script("arguments[0].click();", ColorButton)
        assert browser.find_element(By.XPATH,'//*[@id="myButton"]').get_attribute('style') == 'color: green;'

#4. 텍스트박스에 임의의 메세지 입력 후 입력한 값이 정상적으로 기재되는지 확인
def test_textinput(browser):
    TestMessage = "테스트 메세지"
    text_input = browser.find_element(By.ID,'myTextInput')
    text_input.send_keys(TestMessage)
    time.sleep(0.1)
    writer = text_input.get_attribute("value")
    assert writer == TestMessage

#5. 드랍박스에서 선택한 메뉴에 맞는 상호작용 요소가 반영되는지 확인(여기서는 텍스트만 변경)
def test_mouse_over1(browser):
    one_click = browser.find_element(By.ID,'dropOption1')
    browser.execute_script("arguments[0].click();", one_click)
    one_click_result = browser.find_element(By.XPATH,'//*[@id="tbodyId"]/tr[1]/td[4]/h3').text
    assert one_click_result == "Link One Selected"

#6. 두번째 메뉴도 동일하게 작동하는지 확인
def test_mouse_over2(browser):
    one_click = browser.find_element(By.ID,'dropOption2')
    browser.execute_script("arguments[0].click();", one_click)
    one_click_result = browser.find_element(By.XPATH,'//*[@id="tbodyId"]/tr[1]/td[4]/h3').text
    assert one_click_result == "Link Two Selected"

#7. 세번째 메뉴도 동일하게 작동하는지 확인
def test_mouse_over3(browser):
    one_click = browser.find_element(By.ID,'dropOption3')
    browser.execute_script("arguments[0].click();", one_click)
    one_click_result = browser.find_element(By.XPATH,'//*[@id="tbodyId"]/tr[1]/td[4]/h3').text
    assert one_click_result == "Link Three Selected"

#8. 게이지를 올렸을때 게이지 카운트가 게이지에 맞게 정상적으로 반영되는지 확인
def test_gage_up(browser):
    for i in range(5, 10):
        drag = browser.find_element(By.ID,'mySlider')
        drag.send_keys(Keys.ARROW_RIGHT)
        gage = browser.find_element(By.ID,'progressLabel').text
        gage_count = ''.join([num for num in gage if num.isdigit()])
        i += 1
        assert int(gage_count) == int(i * 10)

#9. 게이지를 내렸을때 게이지 카운트가 게이지에 맞게 정상적으로 반영되는지 확인
def test_gage_down(browser):
    for d in reversed(range(11)):
        drag = browser.find_element(By.ID,'mySlider')
        drag.send_keys(Keys.ARROW_LEFT)
        gage = browser.find_element(By.ID,'progressLabel').text
        gage_count = ''.join([num for num in gage if num.isdigit()])
        if d > 0: d -= 1
        assert int(gage_count) == int(d * 10)

#10. 게이지가 최대치일때 더 올릴려고 시도하면 더 증가하지 않는지 확인
def test_gage_max(browser):
    gage = browser.find_element(By.ID,'progressLabel').text
    drag = browser.find_element(By.ID,'mySlider')
    gage_count = ''.join([num for num in gage if num.isdigit()])
    for i in range(11):
        if int(gage_count) <= 100:
            drag.send_keys(Keys.ARROW_RIGHT)
    if int(gage_count) == 100:
        drag.send.keys(Keys.ARROW_RIGHT)
        assert int(gage_count) == 100

#11. 게이지가 최소치일때 더 내릴려고 시도하면 더 감소하지 않는지 확인
def test_gage_min(browser):
    gage = browser.find_element(By.ID,'progressLabel').text
    drag = browser.find_element(By.ID,'mySlider')
    gage_count = ''.join([num for num in gage if num.isdigit()])
    for i in range(11):
        if int(gage_count) >= 0:
            drag.send_keys(Keys.ARROW_LEFT)
    if int(gage_count) == 0:
        drag.send.keys(Keys.ARROW_LEFT)
        assert int(gage_count) == 0

#12. 드랍박스에서 첫번째 항목을 선택했을때 미터바가 정상적으로 반영되는지 확인
def test_SelectDropdown1(browser):
    Selectbox = browser.find_element(By.ID,'mySelect')
    select = Select(Selectbox)
    select.select_by_value("25%")
    time.sleep(0.1)
    Meter = browser.find_element(By.ID,'meterBar').get_attribute('value')
    assert Meter == "0.25"

#13. 드랍박스에서 두번째 항목을 선택했을때 미터바가 정상적으로 반영되는지 확인
def test_SelectDropdown2(browser):
    Selectbox = browser.find_element(By.ID,'mySelect')
    select = Select(Selectbox)
    select.select_by_value("50%")
    time.sleep(0.1)
    Meter = browser.find_element(By.ID,'meterBar').get_attribute('value')
    assert Meter == "0.5"

#14. 드랍박스에서 세번째 항목을 선택했을때 미터바가 정상적으로 반영되는지 확인
def test_SelectDropdown3(browser):
    Selectbox = browser.find_element(By.ID,'mySelect')
    select = Select(Selectbox)
    select.select_by_value("75%")
    time.sleep(0.1)
    Meter = browser.find_element(By.ID,'meterBar').get_attribute('value')
    assert Meter == "0.75"

#15. 드랍박스에서 네번째 항목을 선택했을때 미터바가 정상적으로 반영되는지 확인
def test_SelectDropdown4(browser):
    Selectbox = browser.find_element(By.ID,'mySelect')
    select = Select(Selectbox)
    select.select_by_value("100%")
    time.sleep(0.1)
    Meter = browser.find_element(By.ID,'meterBar').get_attribute('value')
    assert Meter == "1"

#16. iframe 창으로 스왑했을때 해당 창의 텍스트가 정상적으로 체크되는지 확인
def test_iframe(browser):
    browser.switch_to.frame("myFrame2")
    iframe = browser.find_element(By.XPATH,'/html/body/h4').text
    browser.switch_to.default_content()
    assert iframe == "iFrame Text"

#17. 2개의 라디오버튼 중 하나를 선택했을때 하나만 체크되는지 확인
def test_radio2(browser):
    time.sleep(0.1)
    radio2 = browser.find_element(By.ID,'radioButton2')
    browser.execute_script("arguments[0].click();", radio2)
    radio1status = browser.find_element(By.ID,'radioButton1').get_attribute("checked")
    radio2status = browser.find_element(By.ID,'radioButton2').get_attribute("checked")
    assert radio2status == 'true' and radio1status == None

#18. 2개의 라디오버튼 중 다른 하나를 선택했을때 기존 버튼의 체크 해제도 작동하는지 확인
def test_radio1(browser):
    time.sleep(0.1)
    radio1 = browser.find_element(By.ID,'radioButton1')
    browser.execute_script("arguments[0].click();", radio1)
    radio1status = browser.find_element(By.ID,'radioButton1').get_attribute("checked")
    radio2status = browser.find_element(By.ID,'radioButton2').get_attribute("checked")
    assert radio1status == 'true' and radio2status == None

#19. 숨김 버튼이 켜져있으면 숨김영역이 표시되지 않는지 확인
def test_hiddenON(browser):
    if browser.find_element(By.ID,'checkBox1') == None:
     time.sleep(0.1)
     assert browser.find_element(By.XPATH,'//*[@id="tbodyId"]/tr[10]').get_attribute("tr style") == "display: none;"

#20. 숨김 해제 버튼을 클릭하면 숨김영역이 표시되는지 확인
def test_hiddenOFF(browser):
    hidden = browser.find_element(By.ID,'checkBox1')
    time.sleep(0.1)
    browser.execute_script("arguments[0].click();", hidden)
    assert browser.find_element(By.XPATH,'//*[@id="tbodyId"]/tr[10]').get_attribute("tr style") == None

#21. 드래그앤드롭으로 이미지를 옮겼을때 정상적으로 옮겨지는지 확인
def test_dragndrop(browser):
    drag = browser.find_element(By.XPATH,'//*[@id="logo"]')
    drop = browser.find_element(By.XPATH,'//*[@id="drop2"]')
    time.sleep(1)
    ac = ActionChains(browser)
    ac.drag_and_drop(drag, drop).perform
    time.sleep(1)
    check = bool(browser.find_element(By.XPATH,'//*[@id="drop2"]').find_element(By.XPATH,'//*[@id="logo"]'))
    assert check == True

#22. 체크되지않은 체크박스 형태의 버튼 3개를 다 클릭했을때 3개 다 활성화되는지 확인
def test_checkboxON(browser):
    checkbox2 = browser.find_element(By.ID,'checkBox2')
    browser.execute_script("arguments[0].click();", checkbox2)
    time.sleep(0.1)
    checkbox3 = browser.find_element(By.ID,'checkBox3')
    browser.execute_script("arguments[0].click();", checkbox3)
    time.sleep(0.1)
    checkbox4 = browser.find_element(By.ID,'checkBox4')
    browser.execute_script("arguments[0].click();", checkbox4)
    time.sleep(0.1)
    assert browser.find_element(By.ID,'checkBox2').get_attribute("checked") == "true" \
            and browser.find_element(By.ID,'checkBox3').get_attribute("checked") == "true" \
            and browser.find_element(By.ID,'checkBox4').get_attribute("checked") == "true"

#23. 체크된 체크박스 형태의 버튼을 3개 다 다시 클릭했을때 3개 다 비활성화되는지 확인
def test_checkboxOFF(browser):
    checkbox2 = browser.find_element(By.ID,'checkBox2')
    browser.execute_script("arguments[0].click();", checkbox2)
    time.sleep(0.1)
    checkbox3 = browser.find_element(By.ID,'checkBox3')
    browser.execute_script("arguments[0].click();", checkbox3)
    time.sleep(0.1)
    checkbox4 = browser.find_element(By.ID,'checkBox4')
    browser.execute_script("arguments[0].click();", checkbox4)
    time.sleep(0.1)
    assert browser.find_element(By.ID,'checkBox2').get_attribute("checked") == None \
            and browser.find_element(By.ID,'checkBox3').get_attribute("checked") == None \
            and browser.find_element(By.ID,'checkBox4').get_attribute("checked") == None

 

총 23개의 테스트 케이스를 구동하는 코드고, 테스트 페이지의 특성 상 딥한 뎁스의 엘리멘트를 가져와야하는 경우도 없어 보시다시피 find_element의 양식이 매우 단순하고 직관적입니다.

 

find_element로 엘리멘트를 찾고, get이나 text로 그 엘리멘트의 값을 가져와서 Pytest의 assert로 검증하는 단순한 케이스지만, 제 블로그의 Automated test 분류의 첫 글로는 이정도가 딱 적당하지 않을까 싶었습니다.

 

사실 이 정도로는 프로그래밍 기술이 요구되는게 거의 없어서 소프트웨어 QA로서 시작하고자 할때 많이들 걱정하시는 코드 작성 능력의 필요성에 대해서는 크게 걱정 안해도 될 것 같다고 생각하는데,

사실 저도 아직 소프트웨어 QA쪽에서는 햇병아리다 보니 나중에는 또 어떤 고급 스킬을 필요하게 될 지, 어떤 벽이 있을지, 어떤걸 더 배워야할지 명확한 말씀은 못드리겠습니다.

 

하지만, 일단 간단한 것 부터라도 시작해보는게 중요한 것 아니겠습니까.

 

'Automated Test' 카테고리의 다른 글

테스트 리포트 툴 Allure 소개  (0) 2024.08.20