首页 > 解决方案 > 并行获取每个浏览器的 selenium 驱动程序

问题描述

我正在寻找一种在云测试提供商上并行运行多个浏览器功能的方法。测试是用python编写的。我想我大部分时间都在那里,但我无法让每个浏览器的驱动程序都到tearDown. 该项目将对多个站点进行测试,并针对多个平台、浏览器、浏览器版本和屏幕尺寸运行。现在我有一个概念,大多数可重复的步骤将位于项目的顶层,所有子目录(即单个站点)将只导入全局模块。现在这只是多个浏览器的设置。这样我就可以将每个子目录保留为测试本身。这是我所拥有的一个非常精简的示例。

项目结构看起来像

├── initializer.py
└── sites
    ├── site1
    │   ├── test1.py
    │   ├── test2.py
    │   ├── requirements.txt
    │   ├── setup.py
    │   └── test.py
    └── site2
        ├── test1.py
        ├── test2.py
        ├── requirements.txt
        ├── setup.py
        └── test.py

这将是设置所有内容并导入到子目录/站点的顶层initializer.py

#!/usr/bin/env python

import os
import unittest
from threading import Thread
from selenium import webdriver
from desired_capabilities import *

API_EMAIL = os.environ["CBT_EMAIL"]
API_AUTH = os.environ["CBT_AUTH"]

class Setup(unittest.TestCase):

    def __init__(self):
        self.browsers = []
        self.browsers_waiting = []
        self.threads = []

    def setUp(self):
        for platform in platforms:
            # win
            if platform == "Windows 10":
                for version in win_10_chrome_versions:
                    self.gen_browser("Chrome", version, platform)
                for version in win_10_ff_versions:
                    self.gen_browser("Firefox", version, platform)
                for version in win_10_ie_versions:
                    self.gen_browser("Internet Explorer", version, platform)
            # mac
            elif platform == "Mac OSX 10.12":
                for version in mac_10_12_chrome_versions:
                    self.gen_browser("Chrome", version, platform)
                for version in mac_10_12_ff_versions:
                    self.gen_browser("Firefox", version, platform)
                for version in mac_10_12_safari_versions:
                    self.gen_browser("Safari", version, platform)

        for i, browser in enumerate(browsers):
            thread = Thread(target=self.get_browser_and_wait, args=[browser])
            self.threads.append(thread)
            thread.start()

        for thread in self.threads:
            thread.join()

        print ("\n\tAll browsers are setup and ready\n")

        for i, browser in enumerate(browsers_waiting):
            self.tearDown(browser) # I know this doesn't work, it's the approach I would like. tearDown doesn't allow another argument

    def get_browser_driver(self, caps):
        return webdriver.Remote(
                desired_capabilities=caps,
                command_executor="http://%s:%s@hub.crossbrowsertesting.com:80/wd/hub" % (API_EMAIL, API_AUTH)
            )

    def gen_browser(self, browser, version, platform):
        for resolution in desktop_screen_resolution:
            caps = {}
            caps['platform'] = platform
            caps['browserName'] = browser
            caps['version'] = version
            caps['screenResolution'] = resolution
            caps['name'] = 'Login Form Example'
            caps['build'] = '1.0'
            caps['record_video'] = 'false'
            caps['record_network'] = 'false'

            print ("Browser %s %s is now set up for %s at %s\n" % (browser, version, platform, resolution))

            self.browsers.append(caps)

    def get_browser_and_wait(self, browser_data):
        print ("Starting %s %s for %s at %s\n" % (browser_data["browserName"], browser_data["version"], browser_data["platform"], browser_data["screenResolution"]))
        driver = self.get_browser_driver(browser_data)
        browsers_waiting.append({"data": browser_data, "driver": driver})
        print ("%s %s at %s for %s is ready\n" % (browser_data["browserName"], browser_data["version"], browser_data["screenResolution"], browser_data["platform"]))

        while len(self.browsers_waiting) < len(self.browsers):
            print ("Working on %s for %s.... please wait" % (browser_data["browserName"], browser_data["platform"]))
            time.sleep(10)

    def tearDown(self):
        # This is where I'm stumpped
        # looping through the browsers_waiting doesnt work either
        if (self.driver != None):
            self.driver.close()
            self.driver.quit()

然后这是一个myproject/sites/sitename/test.py基本上是主要的入口点脚本,它将调用所有测试

#!/usr/bin/env python

from unittest import TestSuite, TextTestRunner, TestLoader
from example import exampleTest

def suite():
    loader = TestLoader()
    suite = TestSuite()
    suite.addTest(loader.loadTestsFromModule(exampleTest))
    return suite

if __name__ == '__main__':
    runner = TextTestRunner(verbosity=2)
    runner.run(suite())

这是我在子/站点目录中的示例测试myproject/sites/sitename/example.py

#!/usr/bin/env python

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from initializer import Setup
from elements import Element as element

class exampleTest(Setup):

    def test_Header(self):
        for i, browser in enumerate(Setup().browsers_waiting):
            # this does not work
            driver = browser["driver"]
            self.test_result = None

            try:
                driver.get('https://www.somewebsite.com')
                driver.implicitly_wait(20)

                elem = WebDriverWait(driver, 10).until(
                    EC.presence_of_element_located((By.XPATH, element.searchTitle))
                )

                headerText = elem.text
                expectedHeaderText = 'Some Header'

                self.assertEqual(expectedHeaderText, headerText)
                self.test_result = 'pass'

            except AssertionError as e:
                print ("Something went wrong. Error is %s\n" % e)
                self.test_result = 'fail'
                raise

if __name__ == '__main__':
    unittest.main()

我什至不确定这是否可能,或者我以这种方式设置它的方法甚至是最好的方法。我正在苦苦挣扎的地方是,一旦设置了所有浏览器,就能够调用单独的测试,和/或我无法访问每个浏览器的单独驱动程序。当您将它们全部推入浏览器数组时,self.driver 不再可用。

仅供参考:我在这里从 CBT 获得了很多这个想法:https ://github.com/crossbrowsertesting/selenium-python/blob/master/parallel/multithreaded/parallel.py

标签: pythonselenium

解决方案


我不是线程专家,也不是 python unittest 模块专家,如果我的回答不能帮助您解决问题,非常抱歉。

话虽如此,在您的 example.py 模块中,方法 test_Header 依赖于从初始化模块继承的类 Setup 中定义的实例 dict browsers_waiting。当方法 get_browser_and_wait 被执行时,有问题的 dict 会填充键值对。

所以在这一点上,如果方法 get_browser_and_wait 从未被调用,你正在循环一个空的字典

def test_Header(self):
    for i, browser in enumerate(Setup().browsers_waiting):
        # this does not work
        driver = browser["driver"]
        self.test_result = None

再说一遍,我不知道 unitest 模块是如何工作的,并且这个 dict 在引用时应该有一些值。


推荐阅读