自動化測試(web測試selenium框架)

什麼是selenium?javascript

一個用於Web應用程序測試的工具直接運行在瀏覽器中,就像真正的用戶在操做同樣。支持的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。測試與瀏覽器的兼容性,測試你的應用程序看是否可以很好得工做在不一樣瀏覽器和操做系統之上。測試系統功能,建立衰退測試檢驗軟件功能和用戶需求。php

slenium前世css

早期是直接使用 javascrip 注入技術與瀏覽器打交道,slenium RC啓動一個server 將web元素api轉換爲javascript。在Selenium內核啓動瀏覽器以後注入這段Javascript,由此才實現了Selenium的目的:自動化Web操做。這種Javascript注入技術的缺點是速度不理想,並且穩定性大大依賴於Selenium內核對API翻譯成的Javascript質量高低。html

slenium如今前端

Selenium2.x 提出了WebDriver的概念以後與瀏覽器交互利用原生的API,直接操做瀏覽器頁面裏的元素。不一樣的瀏覽器廠商對Web元素的操做和呈現一些差別直接致使了Selenium WebDriver要分瀏覽器廠商不一樣,而提供不一樣的實現。Selenium3.0發佈後,最大更新點就是幹掉了對selenium rc的支持,後面就一直是webdriver協議,java

WebDriver工做流程python

一、經過WebDriver建立一個瀏覽器服務,remote server
二、腳本啓動時會在新的線程中啓動一個瀏覽器,並綁定特定的端口,沒個瀏覽器有不一樣的端口段。
三、client 建立1個session,在該session中經過http請求向remote server發送restful的請求,remote server解析請求,完成相應操做並返回response。
四、分析response,繼續執行腳本仍是結束執行
command.py:Command類中定義了WebDriver的一些經常使用的常量。git

remote\webdrvier.py:全部瀏覽器webdrvier的基類,其中包含了全部webdriver的api接口github

remote\remote_connection.py:包含啓動Remote WebDrvier server,執行client請求,self._commands是selenium的核心請求參數,根據對應的Command常量,發送不一樣的http請求。web

selenium 中的等待方式:time(固定等待),implicitly_wait(隱式等待),WebDriverWait(顯示等待)

webdriver client的原理

當測試腳本啓動Chrome的時候,selenium-webdriver 會首先在新線程中啓動Chrome瀏覽器。啓動後selenium-webdriver會將Chrome綁定到特定的端口,綁定完成後該chrome實例便做爲webdriver的remote server存在;客戶端(也就是測試腳本)建立1個會話,在該session中經過http請求向remote server發送請求,remote server解析請求,完成相應操做並返回response;客戶端接受response,並分析其返回值以決定是轉到第3步仍是結束腳本;

webdriver是按照server – client的經典設計模式設計的。

server端就是remote server,能夠是任意的瀏覽器。當咱們的腳本啓動瀏覽器後,該瀏覽器就是remote server,它的職責就是等待client(腳本)發送請求並作出相應;

client端簡單說來就是咱們的測試代碼,咱們測試代碼中的一些行爲,好比打開瀏覽器,轉跳到特定的url等操做是以http請求的方式發送給被測試瀏覽器,也就是remote server;remote server接受請求,並執行相應操做,並在response中返回執行狀態、返回值等信息;

selenium的搭建
1.在python中安裝好selenium包 : pip install selenium

2.在配置好瀏覽器的驅動程序根據http://www.imdsx.cn/index.php/2017/08/02/drvier/ 驅動對照表下載Chrome對驅動,並添加在PATH環境變量中,(或者直接把驅動放在pytho的安裝目錄下)

 Firefox驅動下載地址爲:https://github.com/mozilla/geckodriver/releases/       ,

 IE瀏覽器驅動下載地址爲:http://selenium-release.storage.googleapis.com/index.html

編寫如下代碼能打開對應的瀏覽器就配置成功:

from selenium import webdriver
driver = webdriver.Chrome()
driver.get('http://www.imdsx.cn')
google瀏覽器
from selenium import webdriver
driver = webdriver.Firefox()
driver.get('http://www.imdsx.cn')
火狐瀏覽器
from selenium import webdriver
driver = webdriver.Ie()
driver.get('http://www.imdsx.cn')
IE瀏覽器

 selenium定位

import selenium #引用selenium包
from selenium import webdriver#引用包的服務
driver = webdriver.Chrome()#建立瀏覽器 當作咱們的服務端
driver.get('xxxxxxxxxxxxxxx/')#打開對應測試網站,這裏的url 必須帶有http
# 8種單數定位方式
# id進行定位
# driver.find_element_by_id('i1').send_keys('123123')#send_keys向文本框輸入數字
# class 定位方式
# driver.find_element_by_class_name('classname').send_keys('123123')
# name定位方式
# driver.find_element_by_name('name').send_keys('123123')
# 文案定位
# driver.find_element_by_link_text('新建標籤頁面').click()  #.click()表示點擊操做
# 文案包含定位方式
js = 'window.scrollTo(0,0);' # 將滾動條調製最上方
driver.execute_script(js)# 執行寫好的js
# import time
# # time.sleep(2)#界面若是反應比較慢能夠加載等等時間
# driver.find_element_by_partial_link_text('新建標籤').click()
# 標籤名定位 最不經常使用的
# driver.find_element_by_tag_name('input').send_keys('1111')
# xpath 定位
# driver.find_element_by_xpath('//*[@id="i1"]').send_keys('2222')
# css id  定位
# driver.find_element_by_css_selector('#i1').send_keys('2222')
# # css name  定位
# driver.find_element_by_css_selector('name').send_keys()
8種定位方式
xpath定位
xpath(xpath 定位儘可能少用層級定位,若是開發更改了頁面的層級,全部定位都掛了,儘可能屬性定位爲主,層級爲輔助)
        //* 取當前頁面的所有元素
        //*[@id='i1'] id 進行定位 @表明引用屬性
        //*[@placeholder="請經過ID定位元素"] 
        //input[@placeholder="請經過ID定位元素"] 經過 標籤名進一步縮小範圍
        //select[4] 若是存在不惟一的狀況 能夠經過角標進行取值 xpath從1開始取
        //select[@size="4" and @multiple="multiple"]  邏輯定位
xpath定位

當不肯定時能夠只用copy直接複製xpath地址(這個地址至關於層級定位最好少用)

 

css selector定位
css  selector
1.支持ID,class 定位  (# 表明id)  ( . 表示class)
不是說不能用class 若是class屬性在頁面中惟一,那麼是能夠等同於id來使用

2.屬性定位
[placeholder = "請經過ID定位元素"]  屬性定位

3.標籤組合定位
input[placeholder = "請經過ID定位元素"] 縮小範圍 先定位input 在定位屬性

4.多屬性組合定位
Css Selector 的多屬性組合選擇過濾 沒有and 只須要多個[] 鏈接 就能夠
select[size = "4"][multiple = "multiple"] 多屬性確立惟一

5.層級關係定位
經過  >  來區分層級的界定
select>option[value='3']

6.模糊匹配
^= 匹配元素屬性以什麼開頭
input[value ^= ""]
$= 匹配屬性以什麼結尾
input[value$=""]
*= 匹配屬性包含什麼值 input
[value *= ""]
View Code
 

作UI自動化前須要瞭解什麼

1.何時作UI自動化:當項目已經穩定不在大面積修改的狀況下,就能夠接自動化了。

2.作UI自動化須要瞭解什麼知識:前端HTML,CSS,定位方式

 簡單的UI自動化框架

1.bin 目錄下放程序入口
2.lib 目錄下放一些配置文件
3.log 目錄下放日誌
  (HTMLTestRunne :生產網頁的報告頁面,
    logger 生成日誌
    path  配置地址
    pyse 定位頁元素使用的框架
    tool  生成錯誤圖片)
4.pages 目錄下放頁面元素、
5.report  目錄下放文件運行報告  (picture)下放錯誤截圖
6.test_case 目錄下放用例

框架能實現:
1、基於顯示等待 解決不穩定的狀況
(time:固定等待 , implicitly_wait: 隱式等待,webDriveWait:顯示等待)
2.解決維護麻煩 PO(page object)思想(以一個頁面當作一個類,每個頁面的功能點當作一個函數)
3.自動化測試用例和頁面數據源進行分離。

怎麼判斷業務邏輯的成功
舉例說明:假設是登陸的業務邏輯,判斷登陸的特有元素消失 ,就表明登陸成功了。
只有登陸成功了纔會出現的元素 判斷一下他是否出現
文檔說明
from lib.logger import logger
from lib.path import WEBCASEPATH,REPORTPATH
from lib.HTMLTestRunner import HTMLTestRunner
import unittest
from lib.tool import Tool

class Main(object):
    def run(self):
        Tool().clear_picture()
        suite = unittest.TestSuite()
        cases = unittest.defaultTestLoader.discover(WEBCASEPATH)
        print(cases)
        for case in cases:
            print(case)
            suite.addTest(case)
        f = open(REPORTPATH,'wb')
        runner = HTMLTestRunner(f,verbosity=1,title=u'測試報告', description=u'用例執行狀況:')
        runner.run(suite)
        f.flush()
        f.close()
if __name__ == '__main__':
    Main().run()
main
# -*- coding: utf-8 -*-
"""
A TestRunner for use with the Python unit testing framework. It
generates a HTML report to show the result at a glance.

The simplest way to use this is to invoke its main method. E.g.

    import unittest
    import HTMLTestRunner

    ... define your tests ...

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


For more customization options, instantiates a HTMLTestRunner object.
HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.

    # output to a file
    fp = file('my_report.html', 'wb')
    runner = HTMLTestRunner.HTMLTestRunner(
                stream=fp,
                title='My unit test',
                description='This demonstrates the report output by HTMLTestRunner.'
                )

    # Use an external stylesheet.
    # See the Template_mixin class for more customizable options
    runner.STYLESHEET_TMPL = '<link rel="stylesheet" href="my_stylesheet.css" type="text/css">'

    # run the test
    runner.run(my_test_suite)


------------------------------------------------------------------------
Copyright (c) 2004-2007, Wai Yip Tung
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name Wai Yip Tung nor the names of its contributors may be
  used to endorse or promote products derived from this software without
  specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""

# URL: http://tungwaiyip.info/software/HTMLTestRunner.html

__author__ = "Wai Yip Tung"
__version__ = "0.8.3"

"""
Change History
Version 0.8.3
* Use Bootstrap (Zhang Xin)
* Change to Chinese (Zhang Xin)

Version 0.8.2
* Show output inline instead of popup window (Viorel Lupu).

Version in 0.8.1
* Validated XHTML (Wolfgang Borgert).
* Added description of test classes and test cases.

Version in 0.8.0
* Define Template_mixin class for customization.
* Workaround a IE 6 bug that it does not treat <script> block as CDATA.

Version in 0.7.1
* Back port to Python 2.3 (Frank Horowitz).
* Fix missing scroll bars in detail log (Podi).
"""

# TODO: color stderr
# TODO: simplify javascript using ,ore than 1 class in the class attribute?

import datetime
import io
import sys
import time
import unittest
from xml.sax import saxutils


# ------------------------------------------------------------------------
# The redirectors below are used to capture output during testing. Output
# sent to sys.stdout and sys.stderr are automatically captured. However
# in some cases sys.stdout is already cached before HTMLTestRunner is
# invoked (e.g. calling logging.basicConfig). In order to capture those
# output, use the redirectors for the cached stream.
#
# e.g.
#   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
#   >>>

class OutputRedirector(object):
    """ Wrapper to redirect stdout or stderr """

    def __init__(self, fp):
        self.fp = fp

    def write(self, s):
        self.fp.write(s)

    def writelines(self, lines):
        self.fp.writelines(lines)

    def flush(self):
        self.fp.flush()


stdout_redirector = OutputRedirector(sys.stdout)
stderr_redirector = OutputRedirector(sys.stderr)


# ----------------------------------------------------------------------
# Template

class Template_mixin(object):
    """
    Define a HTML template for report customerization and generation.

    Overall structure of an HTML report

    HTML
    +------------------------+
    |<html>                  |
    |  <head>                |
    |                        |
    |   STYLESHEET           |
    |   +----------------+   |
    |   |                |   |
    |   +----------------+   |
    |                        |
    |  </head>               |
    |                        |
    |  <body>                |
    |                        |
    |   HEADING              |
    |   +----------------+   |
    |   |                |   |
    |   +----------------+   |
    |                        |
    |   REPORT               |
    |   +----------------+   |
    |   |                |   |
    |   +----------------+   |
    |                        |
    |   ENDING               |
    |   +----------------+   |
    |   |                |   |
    |   +----------------+   |
    |                        |
    |  </body>               |
    |</html>                 |
    +------------------------+
    """

    STATUS = {
        0: u'經過',
        1: u'失敗',
        2: u'錯誤',
    }

    DEFAULT_TITLE = 'Unit Test Report'
    DEFAULT_DESCRIPTION = ''

    # ------------------------------------------------------------------------
    # HTML Template

    HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>%(title)s</title>
    <meta name="generator" content="%(generator)s"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    %(stylesheet)s
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet">

</head>
<body>
<script language="javascript" type="text/javascript"><!--
output_list = Array();

/* level - 0:Summary; 1:Failed; 2:All */
function showCase(level) {
    trs = document.getElementsByTagName("tr");
    for (var i = 0; i < trs.length; i++) {
        tr = trs[i];
        id = tr.id;
        if (id.substr(0,2) == 'ft') {
            if (level < 1) {
                tr.className = 'hiddenRow';
            }
            else {
                tr.className = '';
            }
        }
        if (id.substr(0,2) == 'pt') {
            if (level > 1) {
                tr.className = '';
            }
            else {
                tr.className = 'hiddenRow';
            }
        }
    }
}


function showClassDetail(cid, count) {
    var id_list = Array(count);
    var toHide = 1;
    for (var i = 0; i < count; i++) {
        tid0 = 't' + cid.substr(1) + '.' + (i+1);
        tid = 'f' + tid0;
        tr = document.getElementById(tid);
        if (!tr) {
            tid = 'p' + tid0;
            tr = document.getElementById(tid);
        }
        id_list[i] = tid;
        if (tr.className) {
            toHide = 0;
        }
    }
    for (var i = 0; i < count; i++) {
        tid = id_list[i];
        if (toHide) {
            if(document.getElementById('div_'+tid) == null){
                document.getElementById(tid).className = 'hiddenRow';
            }else{
                document.getElementById('div_'+tid).style.display = 'none'
                document.getElementById(tid).className = 'hiddenRow';
            }

        }
        else {
            document.getElementById(tid).className = '';
        }
    }
}


function showTestDetail(div_id){
    var details_div = document.getElementById(div_id)
    var displayState = details_div.style.display
    // alert(displayState)
    if (displayState != 'block' ) {
        displayState = 'block'
        details_div.style.display = 'block'
    }
    else {
        details_div.style.display = 'none'
    }
}


function html_escape(s) {
    s = s.replace(/&/g,'&amp;');
    s = s.replace(/</g,'&lt;');
    s = s.replace(/>/g,'&gt;');
    return s;
}

/* obsoleted by detail in <div>
function showOutput(id, name) {
    var w = window.open("", //url
                    name,
                    "resizable,scrollbars,status,width=800,height=450");
    d = w.document;
    d.write("<pre>");
    d.write(html_escape(output_list[id]));
    d.write("\n");
    d.write("<a href='javascript:window.close()'>close</a>\n");
    d.write("</pre>\n");
    d.close();
}
*/
--></script>
<div id="div_base">

%(heading)s
%(report)s
%(ending)s

</div>
</body>
</html>
"""
    # variables: (title, generator, stylesheet, heading, report, ending)


    # ------------------------------------------------------------------------
    # Stylesheet
    #
    # alternatively use a <link> for external style sheet, e.g.
    #   <link rel="stylesheet" href="$url" type="text/css">

    STYLESHEET_TMPL = """
<style type="text/css" media="screen">
body        { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
table       { font-size: 100%; }
pre         { white-space: pre-wrap;word-wrap: break-word; }

/* -- heading ---------------------------------------------------------------------- */
h1 {
    font-size: 16pt;
    color: gray;
}
.heading {
    margin-top: 0ex;
    margin-bottom: 1ex;
}

.heading .attribute {
    margin-top: 1ex;
    margin-bottom: 0;
}

.heading .description {
    margin-top: 2ex;
    margin-bottom: 3ex;
}

/* -- css div popup ------------------------------------------------------------------------ */
a.popup_link {
}

a.popup_link:hover {
    color: red;
}

.popup_window {
    display: none;
    position: relative;
    left: 0px;
    top: 0px;
    /*border: solid #627173 1px; */
    padding: 10px;
    background-color: #E6E6D6;
    font-family: "Lucida Console", "Courier New", Courier, monospace;
    text-align: left;
    font-size: 8pt;
    /* width: 500px;*/
}

}
/* -- report ------------------------------------------------------------------------ */
#show_detail_line {
    margin-top: 3ex;
    margin-bottom: 1ex;
}
#result_table {
    width: 99%;
}
#header_row {
    font-weight: bold;
    color: white;
    background-color: #777;
}
#total_row  { font-weight: bold; }
.passClass  { background-color: #74A474; }
.failClass  { background-color: #FDD283; }
.errorClass { background-color: #FF6600; }
.passCase   { color: #6c6; }
.failCase   { color: #FF6600; font-weight: bold; }
.errorCase  { color: #c00; font-weight: bold; }
.hiddenRow  { display: none; }
.testcase   { margin-left: 2em; }


/* -- ending ---------------------------------------------------------------------- */
#ending {
}

#div_base {
            position:absolute;
            top:0%;
            left:5%;
            right:5%;
            width: auto;
            height: auto;
            margin: -15px 0 0 0;
}
</style>
"""

    # ------------------------------------------------------------------------
    # Heading
    #

    HEADING_TMPL = """<div class='page-header' style='margin-top: 15px;'>
<h1>%(title)s</h1>
%(parameters)s
</div>
<p class='description'>%(description)s</p>

"""  # variables: (title, parameters, description)

    HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
"""  # variables: (name, value)

    # ------------------------------------------------------------------------
    # Report
    #

    REPORT_TMPL = u"""
<div class="btn-group btn-group-sm">
<button class="btn btn-default" onclick='javascript:showCase(0)'>總結</button>
<button class="btn btn-default" onclick='javascript:showCase(1)'>失敗</button>
<button class="btn btn-default" onclick='javascript:showCase(2)'>所有</button>
</div>
<p></p>
<table id='result_table' class="table table-bordered">
<colgroup>
<col align='left' />
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' />
</colgroup>
<tr id='header_row'>
    <td>測試套件/測試用例</td>
    <td>總數</td>
    <td>經過</td>
    <td>失敗</td>
    <td>錯誤</td>
    <td>查看</td>
</tr>
%(test_list)s
<tr id='total_row'>
    <td>總計</td>
    <td>%(count)s</td>
    <td>%(Pass)s</td>
    <td>%(fail)s</td>
    <td>%(error)s</td>
    <td>&nbsp;</td>
</tr>
</table>
"""  # variables: (test_list, count, Pass, fail, error)

    REPORT_CLASS_TMPL = u"""
<tr class='%(style)s'>
    <td>%(desc)s</td>
    <td>%(count)s</td>
    <td>%(Pass)s</td>
    <td>%(fail)s</td>
    <td>%(error)s</td>
    <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">詳情</a></td>
</tr>
"""  # variables: (style, desc, count, Pass, fail, error, cid)

    REPORT_TEST_WITH_OUTPUT_TMPL = r"""
<tr id='%(tid)s' class='%(Class)s'>
    <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
    <td colspan='5' align='center'>

    <!--css div popup start-->
    <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
        %(status)s</a>

    <div id='div_%(tid)s' class="popup_window">
        <div style='text-align: right; color:red;cursor:pointer;height: 200px;position: relative;'>
        <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
           [x]</a>
        </div>
        %(script)s
<a href="#" onclick="window.open('picture/%(png)s','','WIDTH=1034,height=619 TOP=0 left=200')"><IMG SRC=picture/%(png)s style="width: 390px;height: 200px;position: absolute;top: 10px;left: 135px"></a>
    </div>
    <!--css div popup end-->

    </td>
</tr>
"""  # variables: (tid, Class, style, desc, status)

    REPORT_TEST_NO_OUTPUT_TMPL = r"""
<tr id='%(tid)s' class='%(Class)s'>
    <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
    <td colspan='5' align='center'>%(status)s</td>
</tr>
"""  # variables: (tid, Class, style, desc, status)

    REPORT_TEST_OUTPUT_TMPL = r"""
%(id)s: %(output)s
"""  # variables: (id, output)

    # ------------------------------------------------------------------------
    # ENDING
    #

    ENDING_TMPL = """<div id='ending'>&nbsp;</div>"""


# -------------------- The end of the Template class -------------------


TestResult = unittest.TestResult


class _TestResult(TestResult):
    # note: _TestResult is a pure representation of results.
    # It lacks the output and reporting ability compares to unittest._TextTestResult.

    def __init__(self, verbosity=1):
        TestResult.__init__(self)
        self.stdout0 = None
        self.stderr0 = None
        self.success_count = 0
        self.failure_count = 0
        self.error_count = 0
        self.verbosity = verbosity

        # result is a list of result in 4 tuple
        # (
        #   result code (0: success; 1: fail; 2: error),
        #   TestCase object,
        #   Test output (byte string),
        #   stack trace,
        # )
        self.result = []

    def startTest(self, test):
        TestResult.startTest(self, test)
        # just one buffer for both stdout and stderr
        self.outputBuffer = io.StringIO()
        stdout_redirector.fp = self.outputBuffer
        stderr_redirector.fp = self.outputBuffer
        self.stdout0 = sys.stdout
        self.stderr0 = sys.stderr
        sys.stdout = stdout_redirector
        sys.stderr = stderr_redirector

    def complete_output(self):
        """
        Disconnect output redirection and return buffer.
        Safe to call multiple times.
        """
        if self.stdout0:
            sys.stdout = self.stdout0
            sys.stderr = self.stderr0
            self.stdout0 = None
            self.stderr0 = None
        return self.outputBuffer.getvalue()

    def stopTest(self, test):
        # Usually one of addSuccess, addError or addFailure would have been called.
        # But there are some path in unittest that would bypass this.
        # We must disconnect stdout in stopTest(), which is guaranteed to be called.
        self.complete_output()

    def addSuccess(self, test):
        self.success_count += 1
        TestResult.addSuccess(self, test)
        output = self.complete_output()
        self.result.append((0, test, output, ''))
        if self.verbosity > 1:
            sys.stderr.write('ok ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('.')

    def addError(self, test, err):
        self.error_count += 1
        TestResult.addError(self, test, err)
        _, _exc_str = self.errors[-1]
        output = self.complete_output()
        self.result.append((2, test, output, _exc_str))
        if self.verbosity > 1:
            sys.stderr.write('E  ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('E')

    def addFailure(self, test, err):
        self.failure_count += 1
        TestResult.addFailure(self, test, err)
        _, _exc_str = self.failures[-1]
        output = self.complete_output()
        self.result.append((1, test, output, _exc_str))
        if self.verbosity > 1:
            sys.stderr.write('F  ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('F')


from lib.logger import logger


class HTMLTestRunner(Template_mixin):
    """
    """

    def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
        self.stream = stream
        self.verbosity = verbosity
        if title is None:
            self.title = self.DEFAULT_TITLE
        else:
            self.title = title
        if description is None:
            self.description = self.DEFAULT_DESCRIPTION
        else:
            self.description = description

        self.startTime = datetime.datetime.now()

    def run(self, test):
        "Run the given test case or test suite."
        result = _TestResult(self.verbosity)
        test(result)
        self.stopTime = datetime.datetime.now()
        self.generateReport(test, result)
        # print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
        return result

    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 getReportAttributes(self, result):
        """
        Return report attributes as a list of (name, value).
        Override this to add custom attributes.
        """
        startTime = str(self.startTime)[:19]
        if hasattr(self, 'self.stopTime'):
            duration = str(self.stopTime - self.startTime)
        else:
            self.stopTime = datetime.datetime.now()
            duration = str(self.stopTime - self.startTime)
        status = []
        if result.success_count: status.append(u'經過 %s' % result.success_count)
        if result.failure_count: status.append(u'失敗 %s' % result.failure_count)
        if result.error_count:   status.append(u'錯誤 %s' % result.error_count)
        if status:
            status = ' '.join(status)
        else:
            status = 'none'
        return [
            (u'開始時間', startTime),
            (u'運行時長', duration),
            (u'狀態', status),
        ]

    def generateReport(self, test, result):
        report_attrs = self.getReportAttributes(result)
        generator = 'HTMLTestRunner %s' % __version__
        stylesheet = self._generate_stylesheet()
        heading = self._generate_heading(report_attrs)
        report = self._generate_report(result)
        ending = self._generate_ending()
        output = self.HTML_TMPL % dict(
            title=saxutils.escape(self.title),
            generator=generator,
            stylesheet=stylesheet,
            heading=heading,
            report=report,
            ending=ending,
        )
        self.stream.write(output.encode('utf8'))

    def _generate_stylesheet(self):
        return self.STYLESHEET_TMPL

    def _generate_heading(self, report_attrs):
        a_lines = []
        for name, value in report_attrs:
            line = self.HEADING_ATTRIBUTE_TMPL % dict(
                name=saxutils.escape(name),
                value=saxutils.escape(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(self, result):
        rows = []
        sortedResult = self.sortResult(result.result)
        for cid, (cls, cls_results) in enumerate(sortedResult):
            # subtotal for a class
            np = nf = ne = 0
            for n, t, o, e in cls_results:
                if n == 0:
                    np += 1
                elif n == 1:
                    nf += 1
                else:
                    ne += 1

            # format class description
            if cls.__module__ == "__main__":
                name = cls.__name__
            else:
                name = "%s.%s" % (cls.__module__, cls.__name__)
            doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
            desc = doc and '%s: %s' % (name, doc) or name

            row = self.REPORT_CLASS_TMPL % dict(
                style=ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
                desc=desc,
                count=np + nf + ne,
                Pass=np,
                fail=nf,
                error=ne,
                cid='c%s' % (cid + 1),
            )
            rows.append(row)
            from lib.tool import Tool
            pnglist = Tool().error_picture()
            logger.debug(pnglist)
            if len(pnglist) > 0:
                Rewrite_results = []
                for cls_p in cls_results:
                    name = cls_p[1].id().split('.')[-1]
                    for png in pnglist:
                        png_name = png[0].split('.')[0]
                        if png_name == name:
                            tmp = cls_p + png
                            Rewrite_results.append(tmp)
                            break
                    else:
                        tmp = cls_p + ('',)
                        Rewrite_results.append(tmp)
                for tid, (n, t, o, e, png) in enumerate(Rewrite_results):
                    self._generate_report_test(rows, cid, tid, n, t, o, e, png)
            else:
                for tid, (n, t, o, e) in enumerate(cls_results):
                    self._generate_report_test(rows, cid, tid, n, t, o, e)

        report = self.REPORT_TMPL % dict(
            test_list=''.join(rows),
            count=str(result.success_count + result.failure_count + result.error_count),
            Pass=str(result.success_count),
            fail=str(result.failure_count),
            error=str(result.error_count),
        )
        return report

    def _generate_report_test(self, rows, cid, tid, n, t, o, e, png=''):
        # e.g. 'pt1.1', 'ft1.1', etc
        has_output = bool(o or e)
        tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid + 1, tid + 1)
        name = t.id().split('.')[-1]
        doc = t.shortDescription() or ""
        desc = doc and ('%s: %s' % (name, doc)) or name
        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):
            # TODO: some problem with 'string_escape': it escape \n and mess up formating
            # uo = unicode(o.encode('string_escape'))
            uo = o
        else:
            uo = o
        if isinstance(e, str):
            # TODO: some problem with 'string_escape': it escape \n and mess up formating
            # ue = unicode(e.encode('string_escape'))
            ue = e
        else:
            ue = e
        if png:
            script = ''
        else:
            script = self.REPORT_TEST_OUTPUT_TMPL % dict(
                id=tid,
                output=saxutils.escape(uo + ue),
            )

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

    def _generate_ending(self):
        return self.ENDING_TMPL


##############################################################################
# Facilities for running tests from the command line
##############################################################################

# Note: Reuse unittest.TestProgram to launch test. In the future we may
# build our own launcher to support more specific command line
# parameters like test title, CSS, etc.
class TestProgram(unittest.TestProgram):
    """
    A variation of the unittest.TestProgram. Please refer to the base
    class for command line parameters.
    """

    def runTests(self):
        # Pick HTMLTestRunner as the default test runner.
        # base class's testRunner parameter is not useful because it means
        # we have to instantiate HTMLTestRunner before we know self.verbosity.
        if self.testRunner is None:
            self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
        unittest.TestProgram.runTests(self)


main = TestProgram

##############################################################################
# Executing this module from the command line
##############################################################################

if __name__ == "__main__":
    main(module=None)
HTMLTestRunner
from logging import handlers
import logging
from lib.path import WEBLOGPATH


class Logger(object):
    __instance = None

    def __new__(cls, *args, **kwargs):
        if not Logger.__instance:
            Logger.__instance = object.__new__(cls, *args)
        return Logger.__instance

    def __init__(self):
        # 格式化log的模板
        self.formater = logging.Formatter(
            '[%(asctime)s] [%(levelname)s] [%(filename)s:%(funcName)s:%(lineno)d] %(message)s')

        # 聲明一個log對象
        self.logger = logging.getLogger('log')
        # 設置全局log級別
        self.logger.setLevel(logging.DEBUG)

        # 文件log
        self.filelogger = handlers.RotatingFileHandler(WEBLOGPATH,
                                                       maxBytes=5242880,
                                                       backupCount=3
                                                       )
        # 屏幕log
        self.console = logging.StreamHandler()
        # 對屏幕設置級別
        self.console.setLevel(logging.DEBUG)

        self.filelogger.setFormatter(self.formater)
        self.console.setFormatter(self.formater)
        self.logger.addHandler(self.filelogger)
        self.logger.addHandler(self.console)

    def log(self):
        return self.logger


logger = Logger().log()
logger
import os

BASEPATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 報告地址
REPORTPATH = BASEPATH + os.path.sep + 'report' + os.path.sep + 'report.html'

LOGPATH = BASEPATH + os.path.sep + 'log' + os.path.sep

WEBLOGPATH = LOGPATH + 'server.log'

# webcase path
WEBCASEPATH = BASEPATH + os.path.sep + 'test_case'

WEBPICTUREPATH = BASEPATH + os.path.sep + 'report' + os.path.sep + 'picture' + os.path.sep
path
# coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select


class Pyse(object):
    '''
        Pyse framework for the main class, the original
    selenium provided by the method of the two packaging,
    making it easier to use.
    '''

    def __init__(self, browser='ff'):
        '''
        Run class initialization method, the default is proper
        to drive the Firefox browser. Of course, you can also
        pass parameter for other browser, Chrome browser for the "Chrome",
        the Internet Explorer browser for "internet explorer" or "ie".
        '''
        if browser == "firefox" or browser == "ff":
            driver = webdriver.Firefox()
        elif browser == "chrome":
            option = webdriver.ChromeOptions()
            option.add_argument("--start-maximized")
            driver = webdriver.Chrome(chrome_options=option,)
        elif browser == "internet explorer" or browser == "ie":
            driver = webdriver.Ie()
        elif browser == "opera":
            driver = webdriver.Opera()
        elif browser == "phantomjs":
            driver = webdriver.PhantomJS()
        elif browser == 'edge':
            driver = webdriver.Edge()
        try:
            self.driver = driver
        except Exception:
            raise NameError(
                "Not found %s browser,You can enter 'ie', 'ff', 'opera', 'phantomjs', 'edge' or 'chrome'." % browser)

    def element_wait(self, css, secs=5):
        '''
        Waiting for an element to display.

        Usage:
        driver.element_wait("css=>#el",10)
        '''
        if "=>" not in css:
            raise NameError("Positioning syntax errors, lack of '=>'.")

        by = css.split("=>")[0]
        value = css.split("=>")[1]

        if by == "id":
            WebDriverWait(self.driver, secs, 1).until(EC.presence_of_element_located((By.ID, value)))
        elif by == "name":
            WebDriverWait(self.driver, secs, 1).until(EC.presence_of_element_located((By.NAME, value)))
        elif by == "class":
            WebDriverWait(self.driver, secs, 1).until(EC.presence_of_element_located((By.CLASS_NAME, value)))
        elif by == "link_text":
            WebDriverWait(self.driver, secs, 1).until(EC.presence_of_element_located((By.LINK_TEXT, value)))
        elif by == "xpath":
            WebDriverWait(self.driver, secs, 1).until(EC.presence_of_element_located((By.XPATH, value)))
        elif by == "css":
            WebDriverWait(self.driver, secs, 1).until(EC.presence_of_element_located((By.CSS_SELECTOR, value)))
        else:
            raise NameError(
                "Please enter the correct targeting elements,'id','name','class','link_text','xpath','css'.")

    def get_element(self, css):
        '''
        Judge element positioning way, and returns the element.
        '''
        if "=>" not in css:
            raise NameError("Positioning syntax errors, lack of '=>'.")

        by = css.split("=>")[0]
        value = css.split("=>")[1]

        if by == "id":
            element = self.driver.find_element_by_id(value)
        elif by == "name":
            element = self.driver.find_element_by_name(value)
        elif by == "class":
            element = self.driver.find_element_by_class_name(value)
        elif by == "link_text":
            element = self.driver.find_element_by_link_text(value)
        elif by == "xpath":
            element = self.driver.find_element_by_xpath(value)
        elif by == "css":
            element = self.driver.find_element_by_css_selector(value)
        else:
            raise NameError(
                "Please enter the correct targeting elements,'id','name','class','link_text','xpath','css'.")
        return element

    def open(self, url):
        '''
        open url.

        Usage:
        driver.open("https://www.baidu.com")
        '''
        self.driver.get(url)

    def max_window(self):
        '''
        Set browser window maximized.

        Usage:
        driver.max_window()
        '''
        self.driver.maximize_window()

    def set_window(self, wide, high):
        '''
        Set browser window wide and high.

        Usage:
        driver.set_window(wide,high)
        '''
        self.driver.set_window_size(wide, high)

    def type(self, css, text):
        '''
        Operation input box.

        Usage:
        driver.type("css=>#el","selenium")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        el.send_keys(text)

    def clear(self, css):
        '''
        Clear the contents of the input box.

        Usage:
        driver.clear("css=>#el")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        el.clear()

    def click(self, css):
        '''
        It can click any text / image can be clicked
        Connection, check box, radio buttons, and even drop-down box etc..

        Usage:
        driver.click("css=>#el")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        el.click()

    def right_click(self, css):
        '''
        Right click element.

        Usage:
        driver.right_click("css=>#el")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        ActionChains(self.driver).context_click(el).perform()

    def move_to_element(self, css):
        '''
        Mouse over the element.

        Usage:
        driver.move_to_element("css=>#el")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        ActionChains(self.driver).move_to_element(el).perform()

    def double_click(self, css):
        '''
        Double click element.

        Usage:
        driver.double_click("css=>#el")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        ActionChains(self.driver).double_click(el).perform()

    def drag_and_drop(self, el_css, ta_css):
        '''
        Drags an element a certain distance and then drops it.

        Usage:
        driver.drag_and_drop("css=>#el","css=>#ta")
        '''
        self.element_wait(el_css)
        element = self.get_element(el_css)
        self.element_wait(ta_css)
        target = self.get_element(ta_css)
        ActionChains(driver).drag_and_drop(element, target).perform()

    def click_text(self, text):
        '''
        Click the element by the link text

        Usage:
        driver.click_text("新聞")
        '''
        self.driver.find_element_by_partial_link_text(text).click()

    def close(self):
        '''
        Simulates the user clicking the "close" button in the titlebar of a popup
        window or tab.

        Usage:
        driver.close()
        '''
        self.driver.close()

    def quit(self):
        '''
        Quit the driver and close all the windows.

        Usage:
        driver.quit()
        '''
        self.driver.quit()

    def submit(self, css):
        '''
        Submit the specified form.

        Usage:
        driver.submit("css=>#el")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        el.submit()

    def F5(self):
        '''
        Refresh the current page.

        Usage:
        driver.F5()
        '''
        self.driver.refresh()

    def js(self, script):
        '''
        Execute JavaScript scripts.

        Usage:
        driver.js("window.scrollTo(200,1000);")
        '''
        self.driver.execute_script(script)

    def get_attribute(self, css, attribute):
        '''
        Gets the value of an element attribute.

        Usage:
        driver.get_attribute("css=>#el","type")
        '''
        el = self.get_element(css)
        return el.get_attribute(attribute)

    def get_text(self, css):
        '''
        Get element text information.

        Usage:
        driver.get_text("css=>#el")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        return el.text

    def get_display(self, css):
        '''
        Gets the element to display,The return result is true or false.

        Usage:
        driver.get_display("css=>#el")
        '''
        self.element_wait(css)
        el = self.get_element(css)
        return el.is_displayed()

    def get_title(self):
        '''
        Get window title.

        Usage:
        driver.get_title()
        '''
        return self.driver.title

    def get_url(self):
        '''
        Get the URL address of the current page.

        Usage:
        driver.get_url()
        '''
        return self.driver.current_url

    def get_windows_img(self, file_path):
        '''
        Get the current window screenshot.

        Usage:
        driver.get_windows_img()
        '''
        self.driver.get_screenshot_as_file(file_path)

    def wait(self, secs):
        '''
        Implicitly wait.All elements on the page.

        Usage:
        driver.wait(10)
        '''
        self.driver.implicitly_wait(secs)

    def accept_alert(self):
        '''
        Accept warning box.

        Usage:
        driver.accept_alert()
        '''
        self.driver.switch_to.alert.accept()

    def dismiss_alert(self):
        '''
        Dismisses the alert available.

        Usage:
        driver.dismiss_alert()
        '''
        self.driver.switch_to.alert.dismiss()

    def switch_to_frame(self, css):
        '''
        Switch to the specified frame.

        Usage:
        driver.switch_to_frame("css=>#el")
        '''
        self.element_wait(css)
        iframe_el = self.get_element(css)
        self.driver.switch_to.frame(iframe_el)

    def switch_to_frame_out(self):
        '''
        Returns the current form machine form at the next higher level.
        Corresponding relationship with switch_to_frame () method.

        Usage:
        driver.switch_to_frame_out()
        '''
        self.driver.switch_to.default_content()

    def open_new_window(self, css):
        '''
        Open the new window and switch the handle to the newly opened window.

        Usage:
        driver.open_new_window()
        '''
        original_windows = self.driver.current_window_handle
        all_handles = self.driver.window_handles
        for handle in all_handles:
            if handle != original_windows:
                self.driver.switch_to.window(handle)

    # def _save_png(self, name):
    #     self.get_windows_img(name)

    def wait_and_save_exception(self, css, name):
        try:
            self.element_wait(css, secs=5)
            return True
        except Exception as e:
            from lib.path import WEBPICTUREPATH
            self.get_windows_img(WEBPICTUREPATH + name + '.jpg')
            return False

    def wait_and_exception(self, css):
        try:
            self.element_wait(css, secs=10)
            return True
        except Exception as e:
            return False

    def select_by_value(self, css, value):
        self.element_wait(css)
        el = self.get_element(css)
        Select(el).select_by_value(value)



if __name__ == '__main__':
    driver = Pyse("chrome")
pase
import os
from lib.path import WEBPICTUREPATH


class Tool(object):
    def __init__(self):
        self.filelist = os.listdir(WEBPICTUREPATH)

    def error_picture(self):
        picture = []
        for item in self.filelist:
            if item.endswith('.jpg'):
                picture.append((item,))
        return picture

    def clear_picture(self):
        list(map(os.remove, map(lambda file: WEBPICTUREPATH + file, self.filelist)))
tool
'''
主要放頁面的元素
'''
from lib.pyse import Pyse
class Base(object):
    def __init__(self):
        self.pyse = Pyse('chrome')
    def open(self):
        self.pyse.open('http://zbox.imdsx.cn/user-login-Lw==.html1')
        #1.第一步打開瀏覽器,輸入網址
    def quit(self):
        self.pyse.quit()
class LoginPage(Base):
    #一個頁面一個類,每一個功能點定義爲函數
    def send_username(self):
        css = "css=>#account"
        self.pyse.type(css,"admin")
        #定義密碼和登錄按鈕
    def send_passwd(self):
        css = "css=>input[name=""password]"
        self.pyse.type(css,"houyafan123")
    def login(self):
        css = 'css=>#submit'
        self.pyse.click(css)
    def check_login(self):
        css = 'css=>a[href="/user-logout.html"]'
        flag = self.pyse.wait_and_save_exception(css,'test_a_lpgin')
        #校驗元素是否存在
        return  flag



if __name__== '__main__':
    page = LoginPage()
    page.open()
    page.send_username()
    page.send_passwd()
    page.login()
#調試當前頁面
basepage
'''
用到page,unittest  函數
當前頁面寫用例,
'''
from pages.basepage import LoginPage
import unittest
class UiTestter(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        '''
        def setUp(self) 當前類下每一條用例運行時候都要先執行一下
        def tearDown(self) 當前類下每一條用例運行結束要執行一次
        def setUpClass(cls)當前類下全部用例以前運行一次
         def tearDownClass(cls)當前類下全部用例以後運行一次
        '''
        cls.page = LoginPage()
        cls.page.open()

    def test_a_lpgin(self):
        self.page.send_username()
        self.page.send_passwd()
        self.page.login()
        self.assertTrue(self.page.check_login())


    @classmethod
    def tearDownClass(cls):
        cls.page.quit()
test_zbox
相關文章
相關標籤/搜索