多线程自动化测试(Python+unittest+Selenium)

    基于Python+Selenium的UI自动化测试已经实现,测试用例也已经上百,现在的问题是,用例太多,顺序执行下来,跑一遍自动化测试,需要超过1小时,时间太长了,所以考虑多线程运行测试。

    话不多说,上代码,这是主程序:

# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     runTestcases.py
   Description :
   Author :        曾良均
   QQ:             277099728
   Date:          9/27/2021 11:28 AM
-------------------------------------------------
   Change Activity:
                   9/27/2021:
-------------------------------------------------
"""
__author__ = 'ljzeng'


from HTMLTestRunners import *
from sendemail import *
from queryMSSQL import commQuery
import threadpool

global all_result

def all_testcase():
    # 待执行测试用例的目录
    case_dir = ".\\TestCase"
    discover = unittest.defaultTestLoader.discover(case_dir, pattern="*.py", top_level_dir=None)
    return discover


def checkver():
    # 检查IronIntel版本
    dt = 'ironintel'
    sqls = "select appver from SYS_CUSTSITES where COMPANYID='iicon004'"
    vers = commQuery(dt=dt, sqlstr=sqls)
    ver = vers[0][0]
    return ver


def run(case, report, nth=0):
    global all_result
    fp_temp = open(report, "wb")
    runner = HTMLTestRunner(stream=fp_temp,description=u'测试用例结果' + report)
    res = runner.run(case)
    all_result.append(res)
    fp_temp.close()
    sys.stderr.write("run: %s \n" % str(len(all_result)))
    os.system("del %s" % report)


if __name__ == "__main__":
    path = ".\\report"
    isExists = os.path.exists(path)
    if not isExists:
        os.mkdir(path)
    nowtime = datetime.now().strftime("%Y.%m.%d.%H%M%S.%f")[:-3]
    rtime = datetime.now().strftime("%Y.%m.%d %H:%M")
    start_time = datetime.now()
    time_stamp = int(time.time())
    report_path = ".\\report\\result_%s" % nowtime
    os.mkdir(report_path)

    all_result = []
    cases = all_testcase()
    task_pool = threadpool.ThreadPool(5)
    count = 0
    lst = []
    for i,j in zip(cases, range(len(list(cases)))):
        file_path = report_path + '\\' + nowtime + '_' + str(count) + '.html'
        file_path = file_path.replace("\r", r"\r").replace('\n', r'\n')
        count += 1
        # sys.stderr.write("\ncase: %s" % i)
        lst.append(([i, file_path,j],None))

    rqs = threadpool.makeRequests(run, lst)

    [task_pool.putRequest(req) for req in rqs]
    task_pool.wait()

    end_time = datetime.now()

    report_file = report_path + "\\result_%s.html" % nowtime

    fp = open(report_file, "wb")
    merge_html = MergeResult(fp=fp,result_list=all_result,start_time=start_time,end_time=end_time, title=u'测试报告', description=u'用例执行情况:')
    merge_html.make_html()
    fp.close()

    # 发送邮件
    try:
        vers = checkver()
        # sendEmail(ver=vers, report=report_file, runtime=rtime)
    except:
        sys.stderr.write("Email delivery report failed!!")
    finally:
        sys.stderr.write(os.popen('move .\\report\\*.* ' + report_path).read())

    下面是重点:测试报告,因为是多线程运行的测试,每个测试结果需要合并到最终的报告中,根据网上大神的代码,改造:基于HTMLTestRunner,加了合并报告部分,下面是合并报告的代码:

class MergeResult(Template_mixin):

    def __init__(self, fp, result_list, start_time, end_time, title, description):
        """ 都是复制其他class 方法,用到的数值"""
         # 文件指针吧
        self.stream = fp
        self.result_lst = result_list
        self.verbosity = 2
        self.startTime = start_time
        self.run_times = 0
        self.stopTime = end_time
        self.totle_count = 0
        self.pass_count = 0
        self.fail_count = 0
        self.error_count = 0
        self.skip_count = 0
        self.rows = []
        self.title = title
        self.description = description

    def _generate_heading(self, report_attrs):
        a_lines = []
        for name, value in report_attrs:
            line = self.HEADING_ATTRIBUTE_TMPL % dict(
                name=name,
                value=value,
                )
            a_lines.append(line)

        heading = self.HEADING_TMPL % dict(
            title=saxutils.escape(self.title),
            parameters=''.join(a_lines),
            description=saxutils.escape(self.description),
            )

        return heading

    def _generate_report_test(self, rows, cid, tid, n, t, o, e):

            """原版copy"""
            # e.g. 'pt1.1', 'ft1.1', etc
            has_output = bool(o or e)
            if n == 0:
                tmp = "p"
            elif n == 1:
                tmp = "f"
            elif n == 2:
                tmp = "e"
            else:
                tmp = "s"
            # tid = tmp + 't%d.%d.%d' % (self.run_times, cid + 1, tid + 1)    # cid重新取
            tid = tmp + 't%d.%d.%d' % (self.run_times, cid, tid + 1)
            name = t.id().split('.')[-1]
            if self.verbosity > 1:
                doc = getattr(t, '_testMethodDoc', "") or ''
            else:
                doc = ""

            desc = doc and ('%s: %s' % (name, doc)) or name

            if not PY3K:
                if isinstance(desc, str):
                    desc = desc.decode("utf-8")
            tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL

            # o and e should be byte string because they are collected from stdout and stderr?
            if isinstance(o, str):
                # uo = unicode(o.encode('string_escape'))

                if PY3K:
                    uo = o
                else:
                    uo = o.decode('utf-8', 'ignore')
            else:
                uo = o

            if isinstance(e, str):
                # ue = unicode(e.encode('string_escape'))
                if PY3K:
                    ue = e
                elif e.find("Error") != -1 or e.find("Exception") != -1:
                    es = e.decode('utf-8', 'ignore').split('\n')
                    try:
                        if es[-2].find("\\u") != -1 or es[-2].find('"\\u') != -1:
                            es[-2] = es[-2].decode('unicode_escape')
                    except Exception:
                        pass
                    ue = u"\n".join(es)
                else:
                    ue = e.decode('utf-8', 'ignore')
            else:
                ue = e

            script = self.REPORT_TEST_OUTPUT_TMPL % dict(
                id=tid,
                output=saxutils.escape(uo + ue),
            )

            if getattr(t, 'imgs', []):
                # 判断截图列表,如果有则追加
                tmp = u""
                for i, img in enumerate(t.imgs):
                    if i == 0:
                        tmp += """ <img src="https://img-blog.csdnimg.cn/2022010707314313099.jpg" style="display: block;" class="img"/>\n""" % img
                    else:
                        tmp += """ <img src="https://img-blog.csdnimg.cn/2022010707314313099.jpg" style="display: none;" class="img"/>\n""" % img

                imgs = self.IMG_TMPL % dict(imgs=tmp)
            else:
                imgs = u""""""

            row = tmpl % dict(
                tid=tid,
                Class=(n == 0 and 'hiddenRow' or 'none'),
                style=n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'passCase'),
                desc=desc,
                script=script,
                status=self.STATUS[n],
                img=imgs,
            )
            rows.append(row)
            if not has_output:
                return

    def sortResult(self, result_list):
        # unittest does not seems to run in any particular order.
        # Here at least we want to group them together by class.
        rmap = {}
        classes = []

        for n, t, o, e in result_list:
            cls = t.__class__
            if not cls in rmap:
                rmap[cls] = []
                classes.append(cls)
            rmap[cls].append((n, t, o, e))
        r = [(cls, rmap[cls]) for cls in classes]
        return r

    def init_rows(self, c_id, current_result):
        """返回一个用例行 - list"""
        # 引用
        sortedResult = self.sortResult(current_result.result)
        for cid, (cls, cls_results) in enumerate(sortedResult):
            # subtotal for a class
            np = nf = ne = ns = 0
            for n, t, o, e in cls_results:
                if n == 0:
                    np += 1
                elif n == 1:
                    nf += 1
                elif n == 2:
                    ne += 1
                else:
                    ns += 1

            name = cls.__name__

            doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
            desc = doc and '%s: %s' % (name, doc) or name

            if not PY3K:
                if isinstance(desc, str):
                    desc = desc.decode("utf-8")

            row = self.REPORT_CLASS_TMPL % dict(
                style=ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
                desc=desc,
                count=np + nf + ne + ns,
                Pass=np,
                fail=nf,
                skip=ns,
                error=ne,
                cid='c%s.%s' % (self.run_times, c_id),
            )
            self.rows.append(row)

            for tid, (n, t, o, e) in enumerate(cls_results):
                # self._generate_report_test(self.rows, cid, tid, n, t, o, e)
                self._generate_report_test(self.rows, c_id, tid, n, t, o, e)

            self.totle_count += current_result.success_count + current_result.failure_count + current_result.error_count
            self.pass_count += current_result.success_count
            self.fail_count += current_result.failure_count
            self.error_count += current_result.error_count
            self.skip_count += current_result.skip_count

    def rows_to_report(self):

        """每个case行数列数构造 tr td构造"""

        total = self.totle_count
        report = self.REPORT_TMPL % dict(
            test_list=u''.join(self.rows),
            count=str(total),
            Pass=str(self.pass_count),
            Pass_p=self.pass_count * 1.00 / total * 100 if total else 0.0,
            fail=str(self.fail_count),
            error=str(self.error_count),
            skip=str(self.skip_count),
            total=str(total),
            channel=str(self.run_times),
        )
        return report

    def getReportAttributes(self):

        """
        开始时间:xxx
        耗时:xx
        状态:xx
        """
        startTime = str(self.startTime)[:19]
        duration = str(self.stopTime - self.startTime)
        status = []

        # status.append(u'Pass:%s' % self.pass_count)
        # status.append(u'Failure:%s' % self.fail_count)
        # status.append(u'Error:%s' % self.error_count)
        # status.append(u'Skip:%s' % self.skip_count)

        status.append(u'<span class="tj passCase"> Pass</span>:%s' % self.pass_count)
        status.append(u'<span class="tj failCase"> Fail</span>:%s' % self.fail_count)
        status.append(u'<span class="tj errorCase"> Error</span>:%s' % self.error_count)
        status.append(u'<span class="tj skipCase"> Skip</span>:%s' % self.skip_count)
        total = self.totle_count
        if total > 0:
            passed = self.pass_count * 1.000 / total * 100
        else:
            passed = 0.0
        # status.append(u'通过率:%.1f%%' % passed)
        status.append(u'<span class="tj"> 通过率</span>:%.1f%%' % passed)

        if status:
            status = u' '.join(status)
        else:
            status = 'none'

        return [
            (u'开始时间', startTime),
            (u'耗时', duration),
            (u'状态', status),
        ]

    def make_html(self):
        """合并全部result 结果,输出到html"""
        for yy in range(len(self.result_lst)):
            # 重置这个计数器---解决合成的html,不能展开下属行问题
            # self.run_times = (yy + 1) * 1000    # 点击报告上的统计不能展开对应内容
            self.run_times = 1   # 点击报告中的Detail,打开的内容不正确
            self.init_rows(yy+1, self.result_lst[yy])  #   参数yy即为原cid

        report = self.rows_to_report()

        # 顶部左上角按个 开始时间 + 耗时 + 状态
        report_attrs = self.getReportAttributes()
        heading = self._generate_heading(report_attrs)
        # 版本
        generator = 'HTMLTestRunner %s' % __version__
        stylesheet = self.STYLESHEET_TMPL
        # 结尾
        ending = self.ENDING_TMPL
        output = self.HTML_TMPL % dict(
                title=saxutils.escape(self.title),
                generator=generator,
                stylesheet=stylesheet,
                heading=heading,
                report=report,
                ending=ending,
                channel=self.run_times,
        )
        if PY3K:
            self.stream.write(output.encode())
        else:
            self.stream.write(output.encode('utf8'))

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zljun8210

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值