Python Selenium学习笔记

Python Selenium学习笔记

September 26, 2016
Python Selenium UnitTest

如何运行单元测试时只运行一个浏览器,而不是每个TestCase都打开一个浏览器

具体实现方法:

class BaseUnitTest(unittest.TestCase):
    # --实例属性区--
    """是否开启TestCase级别的单驱动模式,默认False
    开启时, 一个TestCase只会打开一个浏览器,也就是method之间是公用浏览器,
    而不是每个测试Method都打开一个浏览器
    """
    SINGLE_DRIVER = False

    # --类方法区--
    @classmethod
    def setUpClass(cls):
        cls.driver = None

    @classmethod
    def tearDownClass(cls):
        if cls.SINGLE_DRIVER:
            BaseUnitTest.__quit_driver(cls.driver)
    
    # --静态方法区--
    @staticmethod
    def __new_driver():
        driver = browser(env.BROWSER, env.DRIVER_PATH)
        driver.maximize_window()
        return driver

    @staticmethod
    def __quit_driver(driver):
        if env.BROWSER.upper() == 'PHANTOMJS':
            # 这里是为了能够真正的退出
            driver.service.process.send_signal(signal.SIGTERM)
        driver.quit()
        
     def setUp(self):
        if self.SINGLE_DRIVER:
            if self.__class__.driver is None:
                self.driver = BaseUnitTest.__new_driver()
                self.__class__.SINGLE_DRIVER = self.SINGLE_DRIVER
                self.__class__.driver = self.driver
            else:
                self.driver = self.__class__.driver
        else:
            self.driver = BaseUnitTest.__new_driver()

    def tearDown(self):
        if not self.SINGLE_DRIVER:
            BaseUnitTest.__quit_driver(self.driver)

如何用多线程运行Selenium测试

  • threading.Thread线程方式,这种方式需要注意的是多个线程之间的相互影响,导致共享值的变化,下面是实现方式(这种有问题,就是不知道合适的时机和方式去关闭driver):
# coding=utf-8

import unittest
import threading
from driver import browser
import env
import signal
import utils
import dataUtils
import sys
from time import ctime


class BaseUnitTest(unittest.TestCase):
    # --实例属性区--
    """是否开启TestCase级别的单驱动模式,默认False
    开启时, 一个TestCase只会打开一个浏览器,也就是method之间是公用浏览器,
    而不是每个测试Method都打开一个浏览器
    """
    SINGLE_DRIVER = False

    # --类方法区--
    @classmethod
    def setUpClass(cls):
        env.thread_local.SINGLE_DRIVER = cls.SINGLE_DRIVER

    @classmethod
    def tearDownClass(cls, is_thread=False):
        if is_thread and env.thread_local.SINGLE_DRIVER:
            BaseUnitTest.__quit_driver(env.thread_local.driver)

    # --静态方法区--
    @staticmethod
    def __new_driver():
        driver = browser(env.BROWSER, env.DRIVER_PATH)
        driver.maximize_window()
        return driver

    @staticmethod
    def __quit_driver(driver):
        if env.BROWSER.upper() == 'PHANTOMJS':
            # 这里是为了能够真正的退出
            driver.service.process.send_signal(signal.SIGTERM)
        driver.quit()

    # --实例方法区--
    # 1.重写父类区
    def __init__(self, methodName='runTest'):
        ''' overide the TestCase __init__ Method
            so need to recall the TestCase __init__ method
        '''
        super(BaseUnitTest, self).__init__(methodName)
        self.__testMethodName = methodName

    def setUp(self):
        if env.thread_local.SINGLE_DRIVER:
            if env.thread_local.driver is None:
                env.thread_local.driver = BaseUnitTest.__new_driver()
            self.driver = env.thread_local.driver
        else:
            self.driver = BaseUnitTest.__new_driver()

    def tearDown(self):
        if not env.thread_local.SINGLE_DRIVER:
            BaseUnitTest.__quit_driver(self.driver)

    # 2.自定义方法区
    def assertEqualUnicode(self, a, b):
        a = dataUtils.decodeToUnicode(a)
        b = dataUtils.decodeToUnicode(b)
        self.assertEqual(a, b)

    # 3.私有方法区
    def __exc_info(self):
        ''' copy from TestCase ,aim to overide the TestCase __call__ method
        '''
        exctype, excvalue, tb = sys.exc_info()
        if sys.platform[:4] == 'java':  # tracebacks look different in Jython
            return (exctype, excvalue, tb)
        newtb = tb.tb_next
        if newtb is None:
            return (exctype, excvalue, tb)
        return (exctype, excvalue, newtb)

    def __call__(self, result=None):
        ''' overide the TestCase __call__ method
            aim to add insert_img_decorator to the test_case method
        '''
        if result is None:
            result = self.defaultTestResult()
        result.startTest(self)
        testMethod = getattr(self, self.__testMethodName)
        newMethod = utils.insert_img_decorator(
            self.__testMethodName)(testMethod)
        try:
            try:
                self.setUp()
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self.__exc_info())
                return

            ok = 0
            try:
                newMethod(self)
                ok = 1
            except self.failureException, e:
                result.addFailure(self, self.__exc_info())
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self.__exc_info())

            try:
                self.tearDown()
            except KeyboardInterrupt:
                raise
            except:
                result.addError(self, self.__exc_info())
                ok = 0
            if ok:
                result.addSuccess(self)
        finally:
            result.stopTest(self)
  • multiprocessing进程方式,这种方式可以避免线程上的值共享被共同访问时的问题,但是需要注意其他问题,就是测试结果如何共享的问题,实现代码如上

unittest执行过程分析-TestLoader

TestLoader.discover发现的结果如下:

<unittest.suite.TestSuite
    tests=[
        <unittest.suite.TestSuite
            tests=[
                <unittest.suite.TestSuite tests=[]>,
                <unittest.suite.TestSuite
                    tests=[
                        <exam_manager_test.ExamManageTest testMethod=test_a_add_exam>,
                        <exam_manager_test.ExamManageTest testMethod=test_b_search_exam>
                        ]
                >
            ]
        >,
        <unittest.suite.TestSuite
            tests=[
                <unittest.suite.TestSuite tests=[]>,
                <unittest.suite.TestSuite
                    tests=[
                        <exam_manager_1_test.ExamManageTest testMethod=test_c_add_exam>,
                        <exam_manager_1_test.ExamManageTest testMethod=test_d_search_exam>
                        ]
                >
            ]
        >
    ]
>

数据知道了,那具体流程图如下:

st=>start: Start
suite=>operation: TestSuite Run
teardownclass=>operation: TestCase TearDownPreClass
setupmodule=>operation: TestCase SetUpModule
setupclass=>operation: TestCase SetUpClass
for=>operation: Iterator TestSuite
cond=>condition: Is TestSuite?
case=>operation: TestCase Run
setup=>operation: TestCase SetUp
teardown=>operation: TestCase TearDown
islastcase=>condition: Is Last TestCase?
teardownmodule=>operation: TestCase TearDownModule
teardownclass2=>operation: TestCase TearDownClass
end=>end: End

st->suite->for->cond
cond(yes,right)->suite(right)
cond(no)->teardownclass->setupmodule->setupclass
setupclass->setup->case->teardown->islastcase
islastcase(no,right)->for(left)
islastcase(yes)->teardownclass2->teardownmodule->end

在扶手电梯上是走还是站?

December 6, 2021
生活 电梯 Python

Python多环境管理

October 16, 2019
Python

如何查看git提交近期的所有的改动或新增的文件-Python实现

September 22, 2016
Git Python