对Selenium测试时使用pytest传入参数


Update: 2020/05/04
之后经过尝试,成功把fixture和unittest.TestCase整合起来了
详情见这里: Mixing Pytest Fixture and unittest.TestCase for Selenium Test

对Selenium测试时使用Pytest传入参数

对Selenium测试时,将命令行参数传递给pytest

使用pytest fixture来传入参数

下面是一个简单的例子显示如何传递参数。首先要写一个conftest.py文件。关于conftest.py可以阅读:What is the use of conftest.py files?

# test/conftest.py
import pytest

def pytest_addoption(parser):
    parser.addoption("--username", action="store", help="input useranme")
    parser.addoption("--password", action="store", help="input password")

@pytest.fixture
def params(request):
    params = {}
    params['username'] = request.config.getoption('--username')
    params['password'] = request.config.getoption('--password')
    if params['username'] is None or params['password'] is None:
        pytest.skip()
    return params
# test/play.py

def test_params(params):
    print(params)
    assert params['username'] == 'test@gmail.com'
    assert params['password'] == '12345'
▶ pytest -s test/play.py --username=test@gmail.com --password=12345
====================================================================================== test session starts ======================================================================================
platform darwin -- Python 3.6.7, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /Users/smap10/Project/strike-esg-head-office
plugins: datadir-1.3.1, cov-2.8.1
collected 1 item                                                                                                                                                                                


test/play.py {'username': 'test@gmail.com', 'password': '12345'}
.

=================================================================================
1 passed in 0.01s =================================================================================

Ok,可以发现上面的能正常输出结果,接下来把play.py重写一下, 添加selenium。

简单的例子

# conftest.py
import pytest

def pytest_addoption(parser):
    parser.addoption("--driver_path", action="store", help="input useranme")
    parser.addoption("--username", action="store", help="input useranme")
    parser.addoption("--password", action="store", help="input password")

@pytest.fixture
def params(request):
    params = {}
    params['driver_path'] = request.config.getoption('--driver_path')
    params['username'] = request.config.getoption('--username')
    params['password'] = request.config.getoption('--password')
    if params['username'] is None or params['password'] is None:
        pytest.skip()
    return params

添加selenium

# play.py
from selenium import webdriver

def test_login(params):
    driver = webdriver.Chrome()
    driver.get("https://semantic-ui.com/examples/login.html")

    emailBox = driver.find_element_by_name("email")
    pwBox = driver.find_element_by_name("password")

    emailBox.send_keys(params['username'])
    pwBox.send_keys(params['password'])
    print('Done!')

结果:

▶ pytest --disable-pytest-warnings -s test/play.py --driver_path /usr/local/bin/chromedriver --username=test@gmail.com --password=12345
====================================================================================== test session starts ======================================================================================
platform darwin -- Python 3.6.7, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /
plugins: datadir-1.3.1, cov-2.8.1
collected 1 item                                                                                                                                                                                

test/play.py Done!
.

================================================================================= 1 passed, 2 warnings in 5.97s =================================================================================

复杂的例子

We will get error when passing parameters to TestCase class (the reason is in Failed Attempts section), so I use the one test_ftse_links() function to test all functions.

要注意,我们不能传递参数给TestCase class,不然会得到error。具体的error在下面失败的尝试里有介绍。所以这里我就用一个 test_ftse_links() 函数来测试ftse_links()里的所有的功能。

# tests/download/conftest.py
import pytest

def pytest_addoption(parser):
    parser.addoption("--driver_path", action="store", help="input useranme")
    parser.addoption("--username", action="store", help="input useranme")
    parser.addoption("--password", action="store", help="input password")

@pytest.fixture
def params(request):
    params = {}
    params['driver_path'] = request.config.getoption('--driver_path')
    params['username'] = request.config.getoption('--username')
    params['password'] = request.config.getoption('--password')
    if params['username'] is None or params['password'] is None:
        pytest.skip()
    return params
# esg_research/download/ftse_links.py
import time
from argparse import ArgumentParser, Namespace
from typing import Any, List
from selenium.webdriver.remote.webdriver import WebDriver
from esg_research.download import utils

def get_links(entities: List[Any]) -> List[Any]:
    # Something
    return output_entities

def get_valid_entities(all_entities: List[Any]) -> List[Any]:
    # Something
    return valid_entities

def get_page_number(driver: WebDriver) -> int:
    # Something
    return int(current_page.text)

def go_next_page(driver: WebDriver) -> None:
    next_button = driver.find_elements_by_xpath('//a[@class="dxp-button dxp-bi"]')
    next_button.click()

def main(args: ) -> None:
      # Something
    print('Done!')

if __name__ == '__main__':
    main()
# tests/download/test_ftse_links.py

import time

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

from esg_research.download import ftse_links


def test_ftse_links(params):
    # Login
    options = Options()
    options.add_argument('--headless')
    driver = webdriver.Chrome(executable_path=params['driver_path'], options=options)
    driver.get('https://qsd.ftserussell.com/Account/Login?login=QSD')
    USERNAME, PASSWORD = params['username'], params['password']
    username = driver.find_element_by_id("UserName")
    passward = driver.find_element_by_id("Password")
    username.send_keys(USERNAME)
    passward.send_keys(PASSWORD)
    form = driver.find_element_by_id("loginForm")
    form.submit()

    # Test for get_valid_entities
    entities = driver.find_elements_by_xpath("//tbody/tr[contains(@id, 'gvClientGrid_DXDataRow')]")
    page_entities_count = len(entities)
    entities = ftse_links.get_valid_entities(entities)
    valid_entities_count = len(entities)
    assert page_entities_count > valid_entities_count

    # Test for get_links
    output_entities = ftse_links.get_links(entities)
    assert output_entities[0]['name'] == '1&1 Drillisch'
    assert output_entities[0]['link'] == 'https://qsd.ftserussell.com/Client/Details/14809'

    # Test for get_page_number
    current_page_no = ftse_links.get_page_number(driver)
    assert current_page_no == 1

    # Test for go_next_page
    ftse_links.go_next_page(driver)
    time.sleep(10)
    next_page_no = ftse_links.get_page_number(driver)
    assert current_page_no + 1 == next_page_no

    driver.quit()

下面是测试结果,就是花的时间比较长

▶ pytest  tests/download/test_ftse_links.py  --driver_path=/usr/local/bin/chromedriver --username=test@gmail.com --password=12345
====================================================================================== test session starts ======================================================================================
platform darwin -- Python 3.6.7, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /Users/smap10/Project/strike-esg-head-office
plugins: datadir-1.3.1, cov-2.8.1
collected 1 item                                                                                                                                                                                


tests/download/test_ftse_links.py .                                                                                                                                                       [100%]


================================================================================= 
1 passed in 302.46s (0:05:02) =================================================================================

We used If we do not pass parameter, the related tests will be skipped.

因为我们在conftest.py里使用了 pytest.skip() ,如果不传入参数的话,test会自动跳过:

▶ pytest  tests/download/test_ftse_htmls.py                                                                     
====================================================================================== test session starts ======================================================================================
platform darwin -- Python 3.6.7, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /Users/smap10/Project/strike-esg-head-office
plugins: datadir-1.3.1, cov-2.8.1
collected 1 item                                                                                                                                                                                


tests/download/test_ftse_htmls.py s                                                                                                                                                       [100%]


=====================================================================================
1 skipped in 0.38s =====================================================================================

失败的尝试

无法将 pytest fixture 传递给 TestCase class

下面是两个失败的例子:

Error case 1:

# play.py
from unittest import TestCase
from selenium import webdriver

class TestParams(TestCase):

    def setUp(self, params):
        self.driver = webdriver.Chrome()
        self.driver.get("https://semantic-ui.com/examples/login.html")
        self.params = params

    def test_login(self):
        emailBox = self.driver.find_element_by_name("email")
        pwBox = self.driver.find_element_by_name("password")
        emailBox.send_keys(self.params['username'])
        pwBox.send_keys(self.params['password'])
        self.driver.implicitly_wait(10)
        print('Done!')

Test result:

▶ pytest --disable-pytest-warnings -s test/play.py --driver_path /usr/local/bin/chromedriver --username=test@gmail.com --password=12345
....
FAILED test/play.py::TestParams::test_login - TypeError: setUp() missing 1 required positional argument: 'params'

Error case 2:

# play.py
from unittest import TestCase
from selenium import webdriver


class TestParams(TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.get("https://semantic-ui.com/examples/login.html")

    def test_login(self, params):
        emailBox = self.driver.find_element_by_name("email")
        pwBox = self.driver.find_element_by_name("password")
        emailBox.send_keys(params['username'])
        pwBox.send_keys(params['password'])
        self.driver.implicitly_wait(10)
        print('Done!')

Test result:

▶ pytest --disable-pytest-warnings -s test/play.py --driver_path /usr/local/bin/chromedriver --username=test@gmail.com --password=12345

FAILED test/play.py::TestParams::test_login - TypeError: test_login() missing 1 required positional argument: 'params'

sys.argv 无法传参数给pytest

使用python来测试,传入的参数应该是没问题的:

# test_play/play.py
import sys
print(sys.argv)

结果:

# output
▶ python -m test_play.play  test@gmail.com password          
['/Users/smap10/Project/strike-esg-head-office/test_play/play.py', 'test@gmail.com', 'password']

但是用pytest测试的话,总是出错:

# play.py

from selenium import webdriver
import sys

def test_login(specifiedEmail, specifiedPW):
    driver = webdriver.Chrome()
    driver.get("https://semantic-ui.com/examples/login.html")
    emailBox = driver.find_element_by_name("email")
    pwBox = driver.find_element_by_name("password")
    emailBox.send_keys(sys.argv[-2])
    pwBox.send_keys(sys.argv[-1])

下面是使用pytest的错误提示,我尝试了很多次,更改了参数的位置,还是不行。总是提示找不到文件,但是文件的位置明明传入的是邮箱。pytest -s也打印不出参数,所以具体原因无法特定。

▶  pytest -m test_play/play.py test@gmail.com password 
====================================================================================== test session starts ======================================================================================
platform darwin -- Python 3.6.7, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /Users/smap10/Project/strike-esg-head-office
plugins: datadir-1.3.1, cov-2.8.1
collected 0 items                                                                                                                                                                               


===================================================================================== no tests ran in 0.01s =====================================================================================
ERROR: file not found: test@gmail.com

欢迎订阅我的博客:RSS feed
知乎: 赤乐君
Blog: BrambleXu
GitHub: BrambleXu
Medium: BrambleXu

参考资料


文章作者: BrambleXu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 BrambleXu !
评论
  目录