简体   繁体   中英

Mocking requests sent by the Selenium WebDriver in Python and having the fake responses displayed in the browser instance driven by the driver instead

I'm currently experimenting with using the Python version of Selenium WebDriver along with the Pytest testing frameworkto do automation testing of web applications. I came across a problem when trying to do HTTP request mocking within my Selenium code. I wrote a module named "Selenium_WebDriver_Mocking_test.py" where I navigate to the official Python website and fill in a search term in the search box at the top of the page and then press Enter to move to the results page. The code works perfectly when I don't try to mock anything. Assuming you have both Selenium and Pytest installed, you can run it from the command line interface by typing the following:

py.test full/path/to/module/Selenium_WebDriver_Mocking_test.py

or by typing the following:

python -m pytest full/path/to/module/Selenium_WebDriver_Mocking_test.py

The code is shown below.

# contents of Selenium_WebDriver_Mocking_test.py

import selenium.webdriver
import selenium.webdriver.common.keys as common
import selenium.webdriver.support.ui as user_interface
import selenium.webdriver.support.expected_conditions as expected_conditions
import selenium.webdriver.common.by as locate
import re
import pytest


browsers = {
    "firefox_driver": selenium.webdriver.Firefox(),
    "chrome_driver": selenium.webdriver.Chrome()
}

website_homepage_url = "http://www.python.org"
title_1 = "Welcome to Python.org"


# Test set-up and tear down functions
@pytest.fixture(scope = 'session', params = browsers.keys())
def browser(request):
    driver = browsers[request.param]
    driver.maximize_window()
    def close_browser():
        driver.quit()
    request.addfinalizer(close_browser)
    return driver


@pytest.mark.parametrize(
    "search_term",
    [
        ("pycon"),
        ("tkinter"),
        ("django"),
    ]
)
def test_mocking_get_request_works_properly(search_term, browser):
    browser.get(website_homepage_url)
    assert title_1 in browser.title                 # Check you are on the right page
    search_text_box = browser.find_element_by_css_selector("#id-search-field")
    search_text_box.send_keys(search_term)
    search_text_box.send_keys(common.Keys.RETURN)
    search_page_title = user_interface.WebDriverWait(browser, 10).until(
            expected_conditions.visibility_of_element_located(
                (locate.By.XPATH, "//h2[contains(., 'Search')]")))
    assert search_page_title.is_displayed()         # Check you are on the right page

I then wrote some HTML for a fake results page and tried to use the HTTPretty tool written by Gabriel Falcao to return that page as a response that appears in the browser instead of the real Python.org results page. The modified code is shown below.

# contents of Selenium_WebDriver_Mocking_test.py

import selenium.webdriver
import selenium.webdriver.common.keys as common
import selenium.webdriver.support.ui as user_interface
import selenium.webdriver.support.expected_conditions as expected_conditions
import selenium.webdriver.common.by as locate
import time
import httpretty
import re
import pytest


browsers = {
    "firefox_driver": selenium.webdriver.Firefox(),
    "chrome_driver": selenium.webdriver.Chrome()
}

website_homepage_url = "http://www.python.org"
title_1 = "Welcome to Python.org"

request_url_pattern = re.compile('https://www.python.org/search/.*')
response_html_lines = ["<!DOCTYPE html>",
                       "<html>",
                       "<head>",
                       "<title>Welcome to my fun page</title>",
                       "<meta charset=\"UTF-8\">"
                       "</head>",
                       "<body>",
                       "<h2>Search Python.org</h2>",
                       "<h2>So far so good (^_^)</h2>",
                       "<img src = \"http://i.imgur.com/EjcKmEj.gif\">",
                       "</body>",
                       "</html>"]
fake_response_html = "\n".join(response_html_lines)


# Test set-up and tear down functions
@pytest.fixture(scope = 'session', params = browsers.keys())
def browser(request):
    driver = browsers[request.param]
    driver.maximize_window()
    def close_browser():
        driver.quit()
    request.addfinalizer(close_browser)
    return driver


@pytest.mark.parametrize(
    "search_term",
    [
        ("pycon"),
        ("tkinter"),
        ("django"),
    ]
)
def test_mocking_get_request_works_properly(search_term, browser):
    httpretty.enable()
    httpretty.register_uri(httpretty.GET,
                           request_url_pattern,
                           body = fake_response_html)
    browser.get(website_homepage_url)
    assert title_1 in browser.title                 # Check you are on the right page
    search_text_box = browser.find_element_by_css_selector("#id-search-field")
    search_text_box.send_keys(search_term)
    search_text_box.send_keys(common.Keys.RETURN)
    search_page_title = user_interface.WebDriverWait(browser, 10).until(
            expected_conditions.visibility_of_element_located(
                (locate.By.XPATH, "//h2[contains(., 'Search')]")))
    assert search_page_title.is_displayed()         # Check you are on the right page
    time.sleep(10)
    httpretty.disable()
    httpretty.reset()

That approach did not work and I got the following message in the log of one of the test iterations:

C:\Python27\python.exe "C:\Program Files (x86)\JetBrains\PyCharm 

Community Edition 4.0.4\helpers\pycharm\pytestrunner.py" -p pytest_teamcity C:/Users/johnsmith/Computer_Code/Python/Automation_Testing/Selenium_WebDriver_Mocking_test.py
Testing started at 10:10 ...
============================= test session starts =============================
platform win32 -- Python 2.7.6 -- py-1.4.26 -- pytest-2.6.4
plugins: xdist
collected 6 items

../../../../../../Users/johnsmith/Computer_Code/Python/Automation_Testing/Selenium_WebDriver_Mocking_test.py F
search_term = 'pycon'
browser = <selenium.webdriver.firefox.webdriver.WebDriver object at 0x0000000002E67EB8>

    @pytest.mark.parametrize(
        "search_term",
        [
            ("pycon"),
            ("tkinter"),
            ("django"),
        ]
    )
    def test_mocking_get_request_works_properly(search_term, browser):
        httpretty.enable()
        httpretty.register_uri(httpretty.GET,
                               request_url_pattern,
                               body = fake_response_html)      
>       browser.get(website_homepage_url)

C:\Users\johnsmith\Computer_Code\Python\Automation_Testing\Selenium_WebDriver_Mocking_test.py:70: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py:187: in get
    self.execute(Command.GET, {'url': url})
C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py:173: in execute
    response = self.command_executor.execute(driver_command, params)
C:\Python27\lib\site-packages\selenium\webdriver\remote\remote_connection.py:349: in execute
    return self._request(command_info[0], url, body=data)
C:\Python27\lib\site-packages\selenium\webdriver\remote\remote_connection.py:379: in _request
    self._conn.request(method, parsed_url.path, body, headers)
C:\Python27\lib\httplib.py:973: in request
    self._send_request(method, url, body, headers)
C:\Python27\lib\httplib.py:1007: in _send_request
    self.endheaders(body)
C:\Python27\lib\httplib.py:969: in endheaders
    self._send_output(message_body)
C:\Python27\lib\httplib.py:829: in _send_output
    self.send(msg)
C:\Python27\lib\httplib.py:791: in send
    self.connect()
C:\Python27\lib\httplib.py:772: in connect
    self.timeout, self.source_address)
C:\Python27\lib\site-packages\httpretty\core.py:477: in create_fake_connection
    s.connect(address)
C:\Python27\lib\site-packages\httpretty\core.py:311: in connect
    self.truesock.connect(self._address)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'connect', self = <socket._socketobject object at 0x000000000385F9A0>
args = (('127.0.0.1', '49952'),)

    def meth(name,self,*args):
>       return getattr(self._sock,name)(*args)
E       TypeError: an integer is required

C:\Python27\lib\socket.py:224: TypeError

I also tried using the responses tool written by David Cramer. While that did not return any errors, it also did not do anything at all and the test proceeded as if no mocking has happened. My question is: is there a way in Python to mock requests sent by the Selenium WebDriver and have the fake response bodies displayed in the browser instance driven by the driver instead ? Any help is appreciated.

For your case you can't use 'standard' mocking approach as you obviously interfere with the webdriver communication. Instead, i suggest you to use https://pypi.python.org/pypi/pytest-localserver so there you actually run local server on some random port and server what is needed for you. I use that technique for testing pytest-splinter (which I also suggest you to use for your tests actually) Here you can see how it all works: https://github.com/pytest-dev/pytest-splinter/blob/master/tests/test_plugin.py

in case you'll use pytest-splinter your code will look like:

   """Testing fake html page with pytest-splinter."""
import pytest
from pytest_localserver.http import WSGIServer
import urlparse

browsers = [
    "firefox",
    "chrome"
]

title_1 = "Welcome to my fun page"

response_html = """
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to my fun page</title>
    <meta charset="UTF-8">
    </head>
    <body>
    <h2>Search Python.org</h2>
    <form action="/">
        <input id="id-search-field" name="search" />
    </form>
    </body>
    </html>
    """


def simple_app(environ, start_response):
    """Simplest possible WSGI application."""
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    query = urlparse.parse_qs(environ['QUERY_STRING'])
    if 'search' in query:
        search_term = query['search'][0]
        return ["""
        <!DOCTYPE html>
        <html>
        <body>
        <h2>So far so good (^_^)</h2>
        <p>You searched for: "{search_term}"</p>
        <img src="http://i.imgur.com/EjcKmEj.gif">
        </body>
        </html>
        """.format(search_term=search_term)]
    else:
        return [response_html]


@pytest.yield_fixture
def testserver():
    """Define the testserver."""
    server = WSGIServer(application=simple_app)
    server.start()
    yield server
    server.stop()


@pytest.fixture(params=browsers)
def splinter_webdriver(request):
    return request.param


@pytest.mark.parametrize(
    "search_term",
    [
        ("pycon"),
        ("tkinter"),
        ("django"),
    ]
)
def test_get_request_works_properly(search_term, browser, testserver):
    browser.visit(testserver.url)
    assert title_1 in browser.title                 # Check you are on the right page
    assert browser.find_by_xpath("//h2[contains(., 'Search')]").visible  # Check you are on the right page
    browser.find_by_css("#id-search-field").fill(search_term + '\r')
    assert browser.find_by_xpath(
        """//p[contains(., 'You searched for: "{search_term}"')]""".format(search_term=search_term)).visible
    assert browser.find_by_xpath(
        "//img[contains(@src, 'i.imgur.com/EjcKmEj.gif')]").visible  # Check you get results

requirements i used for this are:

pytest pytest-splinter pytest-localserver

(all latest)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM