## 目錄 * 簡介 * Gherkin language 是什麼? * 會使用套件與版本 * 建立簡單測試專案 1. 建立 python 虛擬環境 2. 安裝相關套件 3. 撰寫測試腳本 4. 撰寫測試程式集 * E2E 1. selenium 2. pytest fixture 3. 建立簡單E2E測試 4. 專案目錄 5. 執行測試專案 * 參考資料 ### 簡介 > 使用 pytest-bdd 搭配測試, 測試腳本是使用 Gherkin language。 ### Gherkin language 是什麼? > Gherkin 是使用自然語言(英文、中文)對操作行為與使用情境進行描述的簡單結構語法。 ``` Gherkin Feature: Login functionality 測試功能 Scenario: Login to system 情境描述 Given I visit login page 前置條件 When I enter account and password 當事件發生 And I press the login button Then I should see home page 結果 ``` ### 會使用套件與版本 * python 3.9.0 * pytest 7.4.3 * pytest-bdd 7.0.1 * selenium 4.19.0 ### 建立簡單測試專案 1. 建立專案資料夾 ``` mkdir tests cd tests mkdir features functional ``` > 目錄結構 ``` 測試專案結構 tests │ └──features │ │ └──functional ``` #### 建立 python 虛擬環境 1. 進入專案內 > 目前位置為 /tests 2. 建立 python 虛擬環境 ``` python3 -m venv test_venv(名稱) ``` > 目錄會出現 test_venv 資料夾 3. 啟動 python 虛擬環境 ``` source test_venv/bin/activate ``` 4. 環境啟動成功在終端機的指令列前端會出現 (虛擬名稱)。 5. 停用虛擬環境指令 ``` deactivate ``` #### 安裝相關套件 位置: 虛擬環境內的 /tests 目錄下 1. 安裝 pytest ``` pip install pytest ``` 2. 安裝 pytest ``` pip install pytest-bdd ``` 4. 檢查套件是否安裝成功 ``` python -m pip list ``` > 會出現套件列表 ``` Package Version ------------------ ----------- pytest 7.4.3 pytest-bdd 7.0.1 ``` #### 撰寫測試腳本 位置: tests/features 目錄下 1. 新增 login.feature 檔案。 2. 在 login.feature 內使用 Gherkin language 規範進行撰寫測試腳本。 ``` Gherkin Feature: Login functionality Scenario: Login to system Given I visit login page When I enter account and password And I press the login button Then I should see home page ``` #### 撰寫測試程式集 位置: tests/features 目錄下 1. 新增 test_login 檔案。 ``` python import pytest from pytest_bdd import scenarios, given, when, then scenarios('login.feature') @given('I visit login page ') def login(): pass @when('I enter account and password') def set_account_password(): pass @when('I press the login button') def click_button(): pass @then(parsers.parse('I should see the "{title}" page')) def login_success(driver, title): assert 1 == 1 ``` 2. 執行測試 ``` python3 -m pytest ``` ### E2E > E2E 是 End-to-end testing(端對端測試)的簡寫 > 使用 pytest-Bdd 搭配 selenium 進行網站的 E2E #### selenium > selenium 是自動化操作瀏覽器的工具,常用於測試與爬蟲。 > 安裝 selenium ``` pip install selenium ``` #### pytest fixture * fixture 是 pytest 特有的功能,是在測試程式準備階段被呼叫的函式,可用於處理環境設定,如資料庫連線、測試資料集等。fixture 函式會以依賴註入方式帶入測試函式內應用。 * 在特定情境,fixture 會使用 yield 傳送值,如瀏覽器物件的傳遞,為了能正確的關閉。 * yield 惰性求值(lazy evaluation) 的特性,在生成資料傳遞之後會回到函式的特性,來實現 xUnit-style setup/teardown 功能,在一個函式進行建立與關閉。 #### 建立簡單E2E測試 > 以登入測試為例 [完整範例程式](https://github.com/qnsak/example_pytest_bdd/tree/master) #### 專案目錄 ``` tests │ └──features │ │ │ └──login.feature └──functional │ └──test_login.py ``` 1. login.feature ``` feature Feature: Login functionality Scenario: Login to system Given'I visit login page When I enter user in the account field And I enter 123456 in the password field And I press the login button Then I should see the hello ``` 2. test_login.py > 在 pytest-bdd 導入 selenium ``` python import pytest from pytest_bdd import scenarios, given, when, then from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait @pytest.fixture def driver(): # 取得瀏覽器的設定資料 browser_options = Options() # 不顯示瀏覽器 browser_options.add_argument('--headless') # 启动浏览器 browser = webdriver.Chrome(options=browser_options) # 等待瀏覽器開啟 browser.implicitly_wait(20) # 送出瀏覽器物件 yield browser # 关闭浏览器 browser.quit() scenarios('login.feature') @given(parsers.parse('I visit login page')) def set_account(driver, path): driver.get('http://127.0.0.1:3005/'+path) @when(parsers.parse('I enter {account} in the account field')) def set_password(driver, account): driver.find_element(By.NAME, 'name').send_keys(account) @when(parsers.parse('I enter {password} in the password field')) def set_password(driver, password): driver.find_element(By.NAME, 'password').send_keys(password) @when(parsers.parse('I press the login button')) def click_login_button(driver): driver.find_element(By.NAME, 'login_button').click() @then(parsers.parse('I should see the hello')) def login_success(driver, title): data = WebDriverWait(driver,100).until(EC.presence_of_element_located((By.XPATH, '/html/body/h1'))) assert 'hello' == data.text ``` #### 執行測試專案 1. 安裝測試套件 ``` shell # 進入測試專案,建議使用 python 虛擬環境 cd tests # 安裝套件 pip install -r requirements.txt ``` 2. 安裝簡單登入網頁 ``` shell # 進入測試專案,建議使用 python 虛擬環境 cd web # 安裝套件 pip install -r requirements.txt ``` 3. 執行測試 ``` shell # 到測試專案目錄執行測試 pytest ``` ## 參考資料 [PYTHON TESTING 101: PYTEST-BDD](https://automationpanda.com/2018/10/22/python-testing-101-pytest-bdd/) [Pytest-BDD’s documentation!](https://pytest-bdd.readthedocs.io/en/stable/) [pypi](https://pypi.org/project/pytest-bdd/) [pytest-with-eric](https://pytest-with-eric.com/bdd/pytest-bdd/) [從 0 開始培育成為自動化測試工程師的學習指南系列 第 23 篇](https://ithelp.ithome.com.tw/articles/10326971?sc=rss.iron) [pytest fixture](https://blog.tzing.tw/posts/python-testing-pytest-fixture-91b547f2) [automationpanda](https://automationpanda.com/2023/01/12/passing-test-inputs-into-pytest/) [ithome - 關於Gherkin](https://ithelp.ithome.com.tw/articles/10226615)