国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu)

這篇具有很好參考價(jià)值的文章主要介紹了python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

前言:

從前面五篇,對(duì)selenium大概也有了個(gè)理解,如何定位元素,如何操作元素,如何切換句柄。這時(shí)候就要做個(gè)小demo實(shí)戰(zhàn)下了!

功能主要有:

1-PO模式,設(shè)計(jì)page、testcase、testsuit

2-打印log文件

3-生成測(cè)試報(bào)告

4-壓縮測(cè)試報(bào)告,發(fā)送到郵箱

一、項(xiàng)目結(jié)構(gòu)

python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具

二、pages講解

pages是對(duì)要寫自動(dòng)化的頁(yè)面進(jìn)行分離,抽取出來(lái)定位元素,執(zhí)行方法。以login為例子

打開頁(yè)面--->進(jìn)入登錄頁(yè)面--->選擇賬號(hào)登錄---->輸入框輸入用戶名密碼--->點(diǎn)擊登錄

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 # @Time    : 2019-08-05 14:44
 4 # @Author  : zhangxue
 5 # @File    : LoginPage.py
 6 # @Desc    : 登錄頁(yè)面
 7 
 8 import time
 9 from selenium.webdriver.common.by import By
10 from config.url import *
11 from config.config import *
12 from selenium import webdriver
13 from pages.BasePage import BasePage
14 
15 class LoginPage(BasePage):
16     '''用戶登錄頁(yè)面'''
17     # 定位器,通過(guò)元素屬性定位元素對(duì)象
18     login_loc = (By.XPATH, "http://a[@class='header_link console']") #首頁(yè)控制臺(tái)鏈接
19     account_loc = (By.XPATH, "http://div[@class='ant-tabs-nav ant-tabs-nav-animated']//div[2]//span")   #賬號(hào)登錄tab選項(xiàng)
20     username_loc = (By.XPATH, "http://input[@id='username_or_email']")   #用戶名/郵箱/手機(jī)號(hào)input
21     password_loc = (By.XPATH, "http://input[@id='password']")
22     submit_loc = (By.XPATH, "http://div[@class='ant-tabs-tabpane ant-tabs-tabpane-active']//button[@class='ant-btn login-submit-btn ant-btn-primary ant-btn-lg']//span")   #登錄按鈕
23 
24     login_url = baseurl_console + "login"  # 登錄鏈接
25     console_dashboardUrl = baseurl_console + "dashboard"  # 登錄狀態(tài)的url
26 
27     """打開官網(wǎng)首頁(yè)"""
28     def openPage(self):
29         self.open(baseurl_guanwang, title_guanwang)
30 
31     """進(jìn)入登錄頁(yè)面"""
32     def click_console(self):
33         self.find_element(*self.login_loc).click()
34         assert self.assert_page_title(title_console), u"title不正確! %s" % title_console
35         assert self.assert_page_url(self.login_url), "url地址不對(duì)! %s" % self.login_url
36 
37     """選擇賬號(hào)登錄tab"""
38     def click_accountLogin(self):
39         self.find_element(*self.account_loc).click()
40 
41     # 輸入用戶名
42     def input_username(self, username):
43         self.find_element(*self.username_loc).clear()
44         self.find_element(*self.username_loc).click()
45         self.find_element(*self.username_loc).send_keys(username)
46 
47     # 輸入密碼
48     def input_password(self, password):
49         self.find_element(*self.password_loc).clear()
50         self.find_element(*self.password_loc).click()
51         self.find_element(*self.password_loc).send_keys(password)
52 
53     # 點(diǎn)擊登錄
54     def click_submit(self):
55         self.find_element(*self.submit_loc).click()
56 
57     #統(tǒng)一登錄入口,方便其他地方調(diào)用
58     def user_login(self, username, password):
59         self.openPage()
60         self.click_console()
61         time.sleep(3)
62 
63         nowhandle = self.driver.current_window_handle  # 在這里得到當(dāng)前窗口句柄
64         allhandles = self.driver.window_handles  # 獲取所有窗口句柄
65         for handle in allhandles:  # 在所有窗口中查找彈出窗口
66             if handle != nowhandle:
67                 self.driver.switch_to.window(handle)  # 這兩步是在彈出窗口中進(jìn)行的操作,證明我們確實(shí)進(jìn)入了
68 
69         self.click_accountLogin()
70         time.sleep(1)
71         self.input_username(username)
72         self.input_password(password)
73         time.sleep(1)
74         self.click_submit()
75 
76         # 登錄成功后比對(duì)url和title
77         assert self.assert_page_title(title_console), u"title不正確! %s" % title_console
78         assert self.assert_page_url(self.console_dashboardUrl), "url地址不對(duì)! %s" % self.console_dashboardUrl
79 
80         time.sleep(2)
81 
82         self.driver.get_screenshot_as_file(basedir + '/report/screen/登錄.png')
83 
84 
85 
86 # if __name__ == "__main__":
87 #     driver = webdriver.Chrome()
88 #     login = LoginPage(driver)
89 #     login.openPage()
90 #     login.click_console()
91 #     login.click_accountLogin()

三、testcase講解

testcase就是對(duì)pages對(duì)應(yīng)的方法組裝,進(jìn)行case設(shè)計(jì),因?yàn)閘ogin頁(yè)面沒(méi)寫太多case,就以一個(gè)為例子

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 # @Time    : 2019-08-05 15:31
 4 # @Author  : zhangxue
 5 # @File    : test_login.py
 6 # @Desc    : 登錄校驗(yàn)
 7 
 8 import unittest
 9 import time
10 from selenium import webdriver
11 from config.url import *
12 from lib.publicMethod.Log import logger
13 from pages.LoginPage import *
14 from config.config import *
15 
16 class CaseLogin(unittest.TestCase):
17     """控制臺(tái)登錄"""
18 
19     @classmethod
20     def setUpClass(cls):
21         logger.info("##########登錄測(cè)試  開始##############")
22         cls.driver = webdriver.Chrome()
23         cls.username = username
24         cls.password = password
25         cls.loginPage = LoginPage(cls.driver)
26 
27     # 用例執(zhí)行體
28     def test_login(self):
29         self.loginPage.user_login(self.username, self.password)
30 
31 
32     @classmethod
33     def tearDownClass(cls):
34         cls.driver.quit()
35         logger.info("##########登錄測(cè)試  結(jié)束##############")
36 
37 
38 if __name__ == "__main__":
39     unittest.main()

四、testsuite講解

testsuite是對(duì)testcase進(jìn)行組裝,統(tǒng)一的一個(gè)執(zhí)行入口。testsuite有多種組合方法,可以詳細(xì)的去了解下,代碼的注釋中有幾種的講解

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 import unittest
 5 import os
 6 
 7 from lib.publicMethod.HTMLTestRunner_PY3 import HTMLTestRunner
 8 import time
 9 from lib.publicMethod import Send_email
10 
11 if __name__ == '__main__':
12 # 生成報(bào)告文件的參數(shù)
13     basedir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
14     print(basedir)
15 # discover這個(gè)方法可以遞歸的去識(shí)別路徑下所有符合pattern的文件,將這些文件加入套件
16     suite = unittest.defaultTestLoader.discover(basedir+'/test/testcase/', pattern='*.py')
17 
18 # 1、用TestSuite的addTests()方法,將測(cè)試用例組裝成測(cè)試套件,不用TestLoader時(shí),傳入的是case里面的方法名。用TestLoader時(shí)傳入類的類名
19 #    addTest傳入單個(gè)的Testcase方法,addTests傳入Testcase方法數(shù)組
20     #  suite.addTest(TestMathFunc('test_add'))
21     # tests = [TestMathFunc('test_add'),TestMathFunc('test_minus'),TestMathFunc('test_divide')]
22     # suite.addTests(tests)
23 
24 # 2、使用addTests+TestLoader傳入測(cè)試用例,但是TestLoader無(wú)法對(duì)case排序
25 
26     # 2.1  loadTestsFromName傳入‘模塊名.TestCase名’(文件名.里面的類名)
27     # suite.addTests(unittest.TestLoader().loadTestsFromName('test_mathfunc.TestMathFunc'))
28     # # 2.2  loadTestsFromNames就是傳入列表即有多個(gè)testcase時(shí),依次傳入文件
29     # suite.addTests(unittest.TestLoader().loadTestsFromNames(['test_mathfunc.TestMathFunc']))
30     # 2.3  loadTestsFromTestCase傳入TestCase名,(testcae中文件里面的類名)
31     # suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))
32 #     suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TwoMathFun))
33 #     suite.addTests(unittest.TestLoader().loadTestsFromTestCase(Add))
34 
35     # suite.addTest(unittest.TestLoader().loadTestsFromTestCase(UntitledTestCase))
36 # 運(yùn)行suite,這樣寫是將結(jié)果輸出到控制臺(tái)
37     # verbosity參數(shù)可以控制輸出的錯(cuò)誤報(bào)告的詳細(xì)程度,默認(rèn)是1,
38     # 如果設(shè)為0,則不輸出每一用例的執(zhí)行結(jié)果,即沒(méi)有上面的結(jié)果中的第1行;
39     # 如果設(shè)為2,則輸出詳細(xì)的執(zhí)行結(jié)果
40     # runner = unittest.TextTestRunner(verbosity=2)
41     # runner.run(suite)
42 
43 #運(yùn)行suite,并將結(jié)果輸出到Html報(bào)告里
44 #生成報(bào)告文件的參數(shù)
45     report_title = 'facepp-UI自動(dòng)化測(cè)試'
46     desc = '餅圖統(tǒng)計(jì)測(cè)試執(zhí)行情況'
47     report_file = basedir+'/report/faceppUI.html'
48     with open(report_file, 'wb') as report:
49         runner = HTMLTestRunner(stream=report, title=report_title, description=desc)
50         runner.run(suite)
51 
52 
53 # 發(fā)送報(bào)告到郵箱
54     time.sleep(1)
55     Send_email.cr_zip('TestReport.zip',basedir + '/report/')
56     Send_email.send_mail_report("facepp-UI自動(dòng)化測(cè)試!!!")

五、生成html報(bào)告

忽略我的報(bào)錯(cuò),因?yàn)槲椰F(xiàn)在的電腦上沒(méi)有安裝瀏覽器的驅(qū)動(dòng)

python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具

大概就是這個(gè)樣子,餅圖會(huì)將通過(guò)的失敗的錯(cuò)誤的。下面的表格會(huì)展示出所有的case,詳情里會(huì)將錯(cuò)誤信息展示出來(lái)

python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具

六、發(fā)送到郵箱

執(zhí)行成功后,會(huì)發(fā)送到郵箱一封郵件,發(fā)送人、收件人都是可以設(shè)置的。郵件內(nèi)容為測(cè)試結(jié)果,html報(bào)告打包后的壓縮文件,可以下載后,打開查看html報(bào)告

1 # 發(fā)送報(bào)告到郵箱
2     time.sleep(1)
3     Send_email.cr_zip('TestReport.zip',basedir + '/report/')
4     Send_email.send_mail_report("facepp-UI自動(dòng)化測(cè)試!!!")

七、生成日志

python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具

日志格式如上,年月日時(shí)分秒,然后info、error等信息

使用時(shí),引入logger類,然后logger.info("##########登錄測(cè)試 開始##############")即可

八、生成html報(bào)告源碼|發(fā)送到郵箱郵件源碼 |日志源碼

?

生成html報(bào)告

python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具
python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具
  1 # -*- coding: utf-8 -*-
  2 """
  3 A TestRunner for use with the Python unit testing framework. It
  4 generates a HTML report to show the result at a glance.
  5 
  6 The simplest way to use this is to invoke its main method. E.g.
  7 
  8     import unittest
  9     import HTMLTestRunner
 10 
 11     ... define your tests ...
 12 
 13     if __name__ == '__main__':
 14         HTMLTestRunner.main()
 15 
 16 
 17 For more customization options, instantiates a HTMLTestRunner object.
 18 HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.
 19 
 20     # output to a file
 21     fp = file('my_report.html', 'wb')
 22     runner = HTMLTestRunner.HTMLTestRunner(
 23                 stream=fp,
 24                 title='My unit test',
 25                 description='This demonstrates the report output by HTMLTestRunner.'
 26                 )
 27 
 28     # Use an external stylesheet.
 29     # See the Template_mixin class for more customizable options
 30     runner.STYLESHEET_TMPL = '<link rel="stylesheet" href="my_stylesheet.css" type="text/css">'
 31 
 32     # run the test
 33     runner.run(my_test_suite)
 34 
 35 
 36 ------------------------------------------------------------------------
 37 Copyright (c) 2004-2007, Wai Yip Tung
 38 All rights reserved.
 39 
 40 Redistribution and use in source and binary forms, with or without
 41 modification, are permitted provided that the following conditions are
 42 met:
 43 
 44 * Redistributions of source code must retain the above copyright notice,
 45   this list of conditions and the following disclaimer.
 46 * Redistributions in binary form must reproduce the above copyright
 47   notice, this list of conditions and the following disclaimer in the
 48   documentation and/or other materials provided with the distribution.
 49 * Neither the name Wai Yip Tung nor the names of its contributors may be
 50   used to endorse or promote products derived from this software without
 51   specific prior written permission.
 52 
 53 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 54 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 55 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 56 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 57 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 58 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 59 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 60 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 61 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 62 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 63 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 64 """
 65 
 66 # URL: http://tungwaiyip.info/software/HTMLTestRunner.html
 67 
 68 __author__ = "Wai Yip Tung"
 69 __version__ = "0.9.1"
 70 
 71 """
 72 Change History
 73 Version 0.9.1
 74 * 用Echarts添加執(zhí)行情況統(tǒng)計(jì)圖 (灰藍(lán))
 75 
 76 Version 0.9.0
 77 * 改成Python 3.x (灰藍(lán))
 78 
 79 Version 0.8.3
 80 * 使用 Bootstrap稍加美化 (灰藍(lán))
 81 * 改為中文 (灰藍(lán))
 82 
 83 Version 0.8.2
 84 * Show output inline instead of popup window (Viorel Lupu).
 85 
 86 Version in 0.8.1
 87 * Validated XHTML (Wolfgang Borgert).
 88 * Added description of test classes and test cases.
 89 
 90 Version in 0.8.0
 91 * Define Template_mixin class for customization.
 92 * Workaround a IE 6 bug that it does not treat <script> block as CDATA.
 93 
 94 Version in 0.7.1
 95 * Back port to Python 2.3 (Frank Horowitz).
 96 * Fix missing scroll bars in detail log (Podi).
 97 """
 98 
 99 # TODO: color stderr
100 # TODO: simplify javascript using ,ore than 1 class in the class attribute?
101 
102 import datetime
103 import sys
104 import io
105 import time
106 import unittest
107 from xml.sax import saxutils
108 
109 
110 # ------------------------------------------------------------------------
111 # The redirectors below are used to capture output during testing. Output
112 # sent to sys.stdout and sys.stderr are automatically captured. However
113 # in some cases sys.stdout is already cached before HTMLTestRunner is
114 # invoked (e.g. calling logging.basicConfig). In order to capture those
115 # output, use the redirectors for the cached stream.
116 #
117 # e.g.
118 #   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
119 #   >>>
120 
121 class OutputRedirector(object):
122     """ Wrapper to redirect stdout or stderr """
123     def __init__(self, fp):
124         self.fp = fp
125 
126     def write(self, s):
127         self.fp.write(s)
128 
129     def writelines(self, lines):
130         self.fp.writelines(lines)
131 
132     def flush(self):
133         self.fp.flush()
134 
135 stdout_redirector = OutputRedirector(sys.stdout)
136 stderr_redirector = OutputRedirector(sys.stderr)
137 
138 
139 # ----------------------------------------------------------------------
140 # Template
141 
142 
143 class Template_mixin(object):
144     """
145     Define a HTML template for report customerization and generation.
146 
147     Overall structure of an HTML report
148 
149     HTML
150     +------------------------+
151     |<html>                  |
152     |  <head>                |
153     |                        |
154     |   STYLESHEET           |
155     |   +----------------+   |
156     |   |                |   |
157     |   +----------------+   |
158     |                        |
159     |  </head>               |
160     |                        |
161     |  <body>                |
162     |                        |
163     |   HEADING              |
164     |   +----------------+   |
165     |   |                |   |
166     |   +----------------+   |
167     |                        |
168     |   REPORT               |
169     |   +----------------+   |
170     |   |                |   |
171     |   +----------------+   |
172     |                        |
173     |   ENDING               |
174     |   +----------------+   |
175     |   |                |   |
176     |   +----------------+   |
177     |                        |
178     |  </body>               |
179     |</html>                 |
180     +------------------------+
181     """
182 
183     STATUS = {
184         0: u'通過(guò)',
185         1: u'失敗',
186         2: u'錯(cuò)誤',
187     }
188 
189     DEFAULT_TITLE = 'Unit Test Report'
190     DEFAULT_DESCRIPTION = ''
191 
192     # ------------------------------------------------------------------------
193     # HTML Template
194 
195     HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
196 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
197 <html xmlns="http://www.w3.org/1999/xhtml">
198 <head>
199     <title>%(title)s</title>
200     <meta name="generator" content="%(generator)s"/>
201     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
202     
203     <link  rel="stylesheet">
204     <script src="https://cdn.bootcss.com/echarts/3.8.5/echarts.common.min.js"></script>
205     <!-- <script type="text/javascript" src="js/echarts.common.min.js"></script> -->
206     
207     %(stylesheet)s
208     
209 </head>
210 <body>
211     <script language="javascript" type="text/javascript"><!--
212     output_list = Array();
213 
214     /* level - 0:Summary; 1:Failed; 2:All */
215     function showCase(level) {
216         trs = document.getElementsByTagName("tr");
217         for (var i = 0; i < trs.length; i++) {
218             tr = trs[i];
219             id = tr.id;
220             if (id.substr(0,2) == 'ft') {
221                 if (level < 1) {
222                     tr.className = 'hiddenRow';
223                 }
224                 else {
225                     tr.className = '';
226                 }
227             }
228             if (id.substr(0,2) == 'pt') {
229                 if (level > 1) {
230                     tr.className = '';
231                 }
232                 else {
233                     tr.className = 'hiddenRow';
234                 }
235             }
236         }
237     }
238 
239 
240     function showClassDetail(cid, count) {
241         var id_list = Array(count);
242         var toHide = 1;
243         for (var i = 0; i < count; i++) {
244             tid0 = 't' + cid.substr(1) + '.' + (i+1);
245             tid = 'f' + tid0;
246             tr = document.getElementById(tid);
247             if (!tr) {
248                 tid = 'p' + tid0;
249                 tr = document.getElementById(tid);
250             }
251             id_list[i] = tid;
252             if (tr.className) {
253                 toHide = 0;
254             }
255         }
256         for (var i = 0; i < count; i++) {
257             tid = id_list[i];
258             if (toHide) {
259                 document.getElementById('div_'+tid).style.display = 'none'
260                 document.getElementById(tid).className = 'hiddenRow';
261             }
262             else {
263                 document.getElementById(tid).className = '';
264             }
265         }
266     }
267 
268 
269     function showTestDetail(div_id){
270         var details_div = document.getElementById(div_id)
271         var displayState = details_div.style.display
272         // alert(displayState)
273         if (displayState != 'block' ) {
274             displayState = 'block'
275             details_div.style.display = 'block'
276         }
277         else {
278             details_div.style.display = 'none'
279         }
280     }
281 
282 
283     function html_escape(s) {
284         s = s.replace(/&/g,'&amp;');
285         s = s.replace(/</g,'&lt;');
286         s = s.replace(/>/g,'&gt;');
287         return s;
288     }
289 
290     /* obsoleted by detail in <div>
291     function showOutput(id, name) {
292         var w = window.open("", //url
293                         name,
294                         "resizable,scrollbars,status,width=800,height=450");
295         d = w.document;
296         d.write("<pre>");
297         d.write(html_escape(output_list[id]));
298         d.write("\n");
299         d.write("<a href='javascript:window.close()'>close</a>\n");
300         d.write("</pre>\n");
301         d.close();
302     }
303     */
304     --></script>
305 
306     <div id="div_base">
307         %(heading)s
308         %(report)s
309         %(ending)s
310         %(chart_script)s
311     </div>
312 </body>
313 </html>
314 """  # variables: (title, generator, stylesheet, heading, report, ending, chart_script)
315 
316     ECHARTS_SCRIPT = """
317     <script type="text/javascript">
318         // 基于準(zhǔn)備好的dom,初始化echarts實(shí)例
319         var myChart = echarts.init(document.getElementById('chart'));
320 
321         // 指定圖表的配置項(xiàng)和數(shù)據(jù)
322         var option = {
323             title : {
324                 text: '測(cè)試執(zhí)行情況',
325                 x:'center'
326             },
327             tooltip : {
328                 trigger: 'item',
329                 formatter: "{a} <br/> : {c} (n5n3t3z%%)"
330             },
331             color: ['#95b75d', 'grey', '#b64645'],
332             legend: {
333                 orient: 'vertical',
334                 left: 'left',
335                 data: ['通過(guò)','失敗','錯(cuò)誤']
336             },
337             series : [
338                 {
339                     name: '測(cè)試執(zhí)行情況',
340                     type: 'pie',
341                     radius : '60%%',
342                     center: ['50%%', '60%%'],
343                     data:[
344                         {value:%(Pass)s, name:'通過(guò)'},
345                         {value:%(fail)s, name:'失敗'},
346                         {value:%(error)s, name:'錯(cuò)誤'}
347                     ],
348                     itemStyle: {
349                         emphasis: {
350                             shadowBlur: 10,
351                             shadowOffsetX: 0,
352                             shadowColor: 'rgba(0, 0, 0, 0.5)'
353                         }
354                     }
355                 }
356             ]
357         };
358 
359         // 使用剛指定的配置項(xiàng)和數(shù)據(jù)顯示圖表。
360         myChart.setOption(option);
361     </script>
362     """  # variables: (Pass, fail, error)
363 
364     # ------------------------------------------------------------------------
365     # Stylesheet
366     #
367     # alternatively use a <link> for external style sheet, e.g.
368     #   <link rel="stylesheet" href="$url" type="text/css">
369 
370     STYLESHEET_TMPL = """
371 <style type="text/css" media="screen">
372     body        { font-family: Microsoft YaHei,Consolas,arial,sans-serif; font-size: 80%; }
373     table       { font-size: 100%; }
374     pre         { white-space: pre-wrap;word-wrap: break-word; }
375 
376     /* -- heading ---------------------------------------------------------------------- */
377     h1 {
378         font-size: 16pt;
379         color: gray;
380     }
381     .heading {
382         margin-top: 0ex;
383         margin-bottom: 1ex;
384     }
385 
386     .heading .attribute {
387         margin-top: 1ex;
388         margin-bottom: 0;
389     }
390 
391     .heading .description {
392         margin-top: 2ex;
393         margin-bottom: 3ex;
394     }
395 
396     /* -- css div popup ------------------------------------------------------------------------ */
397     a.popup_link {
398     }
399 
400     a.popup_link:hover {
401         color: red;
402     }
403 
404     .popup_window {
405         display: none;
406         position: relative;
407         left: 0px;
408         top: 0px;
409         /*border: solid #627173 1px; */
410         padding: 10px;
411         /*background-color: #E6E6D6; */
412         font-family: "Lucida Console", "Courier New", Courier, monospace;
413         text-align: left;
414         font-size: 8pt;
415         /* width: 500px;*/
416     }
417 
418     }
419     /* -- report ------------------------------------------------------------------------ */
420     #show_detail_line {
421         margin-top: 3ex;
422         margin-bottom: 1ex;
423     }
424     #result_table {
425         width: 99%;
426     }
427     #header_row {
428         font-weight: bold;
429         color: #303641;
430         background-color: #ebebeb;
431     }
432     #total_row  { font-weight: bold; }
433     .passClass  { background-color: #bdedbc; }
434     .failClass  { background-color: #ffefa4; }
435     .errorClass { background-color: #ffc9c9; }
436     .passCase   { color: #6c6; }
437     .failCase   { color: #FF6600; font-weight: bold; }
438     .errorCase  { color: #c00; font-weight: bold; }
439     .hiddenRow  { display: none; }
440     .testcase   { margin-left: 2em; }
441 
442 
443     /* -- ending ---------------------------------------------------------------------- */
444     #ending {
445     }
446 
447     #div_base {
448                 position:absolute;
449                 top:0%;
450                 left:5%;
451                 right:5%;
452                 width: auto;
453                 height: auto;
454                 margin: -15px 0 0 0;
455     }
456 </style>
457 """
458 
459     # ------------------------------------------------------------------------
460     # Heading
461     #
462 
463     HEADING_TMPL = """
464     <div class='page-header'>
465         <h1>%(title)s</h1>
466     %(parameters)s
467     </div>
468     <div style="float: left;width:50%%;"><p class='description'>%(description)s</p></div>
469     <div id="chart" style="width:50%%;height:400px;float:left;"></div>
470 """  # variables: (title, parameters, description)
471 
472     HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
473 """  # variables: (name, value)
474 
475     # ------------------------------------------------------------------------
476     # Report
477     #
478 
479     REPORT_TMPL = u"""
480     <div class="btn-group btn-group-sm">
481         <button class="btn btn-default" onclick='javascript:showCase(0)'>總結(jié)</button>
482         <button class="btn btn-default" onclick='javascript:showCase(1)'>失敗</button>
483         <button class="btn btn-default" onclick='javascript:showCase(2)'>全部</button>
484     </div>
485     <p></p>
486     <table id='result_table' class="table table-bordered">
487         <colgroup>
488             <col align='left' />
489             <col align='right' />
490             <col align='right' />
491             <col align='right' />
492             <col align='right' />
493             <col align='right' />
494         </colgroup>
495         <tr id='header_row'>
496             <td>測(cè)試套件/測(cè)試用例</td>
497             <td>總數(shù)</td>
498             <td>通過(guò)</td>
499             <td>失敗</td>
500             <td>錯(cuò)誤</td>
501             <td>查看</td>
502         </tr>
503         %(test_list)s
504         <tr id='total_row'>
505             <td>總計(jì)</td>
506             <td>%(count)s</td>
507             <td>%(Pass)s</td>
508             <td>%(fail)s</td>
509             <td>%(error)s</td>
510             <td>&nbsp;</td>
511         </tr>
512     </table>
513 """  # variables: (test_list, count, Pass, fail, error)
514 
515     REPORT_CLASS_TMPL = u"""
516     <tr class='%(style)s'>
517         <td>%(desc)s</td>
518         <td>%(count)s</td>
519         <td>%(Pass)s</td>
520         <td>%(fail)s</td>
521         <td>%(error)s</td>
522         <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">詳情</a></td>
523     </tr>
524 """  # variables: (style, desc, count, Pass, fail, error, cid)
525 
526     REPORT_TEST_WITH_OUTPUT_TMPL = r"""
527 <tr id='%(tid)s' class='%(Class)s'>
528     <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
529     <td colspan='5' align='center'>
530 
531     <!--css div popup start-->
532     <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
533         %(status)s</a>
534 
535     <div id='div_%(tid)s' class="popup_window">
536         <pre>%(script)s</pre>
537     </div>
538     <!--css div popup end-->
539 
540     </td>
541 </tr>
542 """  # variables: (tid, Class, style, desc, status)
543 
544     REPORT_TEST_NO_OUTPUT_TMPL = r"""
545 <tr id='%(tid)s' class='%(Class)s'>
546     <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
547     <td colspan='5' align='center'>%(status)s</td>
548 </tr>
549 """  # variables: (tid, Class, style, desc, status)
550 
551     REPORT_TEST_OUTPUT_TMPL = r"""%(id)s: %(output)s"""  # variables: (id, output)
552 
553     # ------------------------------------------------------------------------
554     # ENDING
555     #
556 
557     ENDING_TMPL = """<div id='ending'>&nbsp;</div>"""
558 
559 # -------------------- The end of the Template class -------------------
560 
561 
562 TestResult = unittest.TestResult
563 
564 
565 class _TestResult(TestResult):
566     # note: _TestResult is a pure representation of results.
567     # It lacks the output and reporting ability compares to unittest._TextTestResult.
568 
569     def __init__(self, verbosity=1):
570         TestResult.__init__(self)
571         self.stdout0 = None
572         self.stderr0 = None
573         self.success_count = 0
574         self.failure_count = 0
575         self.error_count = 0
576         self.verbosity = verbosity
577 
578         # result is a list of result in 4 tuple
579         # (
580         #   result code (0: success; 1: fail; 2: error),
581         #   TestCase object,
582         #   Test output (byte string),
583         #   stack trace,
584         # )
585         self.result = []
586         self.subtestlist = []
587 
588     def startTest(self, test):
589         TestResult.startTest(self, test)
590         # just one buffer for both stdout and stderr
591         self.outputBuffer = io.StringIO()
592         stdout_redirector.fp = self.outputBuffer
593         stderr_redirector.fp = self.outputBuffer
594         self.stdout0 = sys.stdout
595         self.stderr0 = sys.stderr
596         sys.stdout = stdout_redirector
597         sys.stderr = stderr_redirector
598 
599     def complete_output(self):
600         """
601         Disconnect output redirection and return buffer.
602         Safe to call multiple times.
603         """
604         if self.stdout0:
605             sys.stdout = self.stdout0
606             sys.stderr = self.stderr0
607             self.stdout0 = None
608             self.stderr0 = None
609         return self.outputBuffer.getvalue()
610 
611     def stopTest(self, test):
612         # Usually one of addSuccess, addError or addFailure would have been called.
613         # But there are some path in unittest that would bypass this.
614         # We must disconnect stdout in stopTest(), which is guaranteed to be called.
615         self.complete_output()
616 
617     def addSuccess(self, test):
618         if test not in self.subtestlist:
619             self.success_count += 1
620             TestResult.addSuccess(self, test)
621             output = self.complete_output()
622             self.result.append((0, test, output, ''))
623             if self.verbosity > 1:
624                 sys.stderr.write('ok ')
625                 sys.stderr.write(str(test))
626                 sys.stderr.write('\n')
627             else:
628                 sys.stderr.write('.')
629 
630     def addError(self, test, err):
631         self.error_count += 1
632         TestResult.addError(self, test, err)
633         _, _exc_str = self.errors[-1]
634         output = self.complete_output()
635         self.result.append((2, test, output, _exc_str))
636         if self.verbosity > 1:
637             sys.stderr.write('E  ')
638             sys.stderr.write(str(test))
639             sys.stderr.write('\n')
640         else:
641             sys.stderr.write('E')
642 
643     def addFailure(self, test, err):
644         self.failure_count += 1
645         TestResult.addFailure(self, test, err)
646         _, _exc_str = self.failures[-1]
647         output = self.complete_output()
648         self.result.append((1, test, output, _exc_str))
649         if self.verbosity > 1:
650             sys.stderr.write('F  ')
651             sys.stderr.write(str(test))
652             sys.stderr.write('\n')
653         else:
654             sys.stderr.write('F')
655 
656     def addSubTest(self, test, subtest, err):
657         if err is not None:
658             if getattr(self, 'failfast', False):
659                 self.stop()
660             if issubclass(err[0], test.failureException):
661                 self.failure_count += 1
662                 errors = self.failures
663                 errors.append((subtest, self._exc_info_to_string(err, subtest)))
664                 output = self.complete_output()
665                 self.result.append((1, test, output + '\nSubTestCase Failed:\n' + str(subtest),
666                                     self._exc_info_to_string(err, subtest)))
667                 if self.verbosity > 1:
668                     sys.stderr.write('F  ')
669                     sys.stderr.write(str(subtest))
670                     sys.stderr.write('\n')
671                 else:
672                     sys.stderr.write('F')
673             else:
674                 self.error_count += 1
675                 errors = self.errors
676                 errors.append((subtest, self._exc_info_to_string(err, subtest)))
677                 output = self.complete_output()
678                 self.result.append(
679                     (2, test, output + '\nSubTestCase Error:\n' + str(subtest), self._exc_info_to_string(err, subtest)))
680                 if self.verbosity > 1:
681                     sys.stderr.write('E  ')
682                     sys.stderr.write(str(subtest))
683                     sys.stderr.write('\n')
684                 else:
685                     sys.stderr.write('E')
686             self._mirrorOutput = True
687         else:
688             self.subtestlist.append(subtest)
689             self.subtestlist.append(test)
690             self.success_count += 1
691             output = self.complete_output()
692             self.result.append((0, test, output + '\nSubTestCase Pass:\n' + str(subtest), ''))
693             if self.verbosity > 1:
694                 sys.stderr.write('ok ')
695                 sys.stderr.write(str(subtest))
696                 sys.stderr.write('\n')
697             else:
698                 sys.stderr.write('.')
699 
700 
701 class HTMLTestRunner(Template_mixin):
702 
703     def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
704         self.stream = stream
705         self.verbosity = verbosity
706         if title is None:
707             self.title = self.DEFAULT_TITLE
708         else:
709             self.title = title
710         if description is None:
711             self.description = self.DEFAULT_DESCRIPTION
712         else:
713             self.description = description
714 
715         self.startTime = datetime.datetime.now()
716 
717     def run(self, test):
718         "Run the given test case or test suite."
719         result = _TestResult(self.verbosity)
720         test(result)
721         self.stopTime = datetime.datetime.now()
722         self.generateReport(test, result)
723         print('\nTime Elapsed: %s' % (self.stopTime-self.startTime), file=sys.stderr)
724         return result
725 
726     def sortResult(self, result_list):
727         # unittest does not seems to run in any particular order.
728         # Here at least we want to group them together by class.
729         rmap = {}
730         classes = []
731         for n,t,o,e in result_list:
732             cls = t.__class__
733             if cls not in rmap:
734                 rmap[cls] = []
735                 classes.append(cls)
736             rmap[cls].append((n,t,o,e))
737         r = [(cls, rmap[cls]) for cls in classes]
738         return r
739 
740     def getReportAttributes(self, result):
741         """
742         Return report attributes as a list of (name, value).
743         Override this to add custom attributes.
744         """
745         startTime = str(self.startTime)[:19]
746         duration = str(self.stopTime - self.startTime)
747         status = []
748         if result.success_count: status.append(u'通過(guò) %s' % result.success_count)
749         if result.failure_count: status.append(u'失敗 %s' % result.failure_count)
750         if result.error_count:   status.append(u'錯(cuò)誤 %s' % result.error_count  )
751         if status:
752             status = ' '.join(status)
753         else:
754             status = 'none'
755         return [
756             (u'開始時(shí)間', startTime),
757             (u'運(yùn)行時(shí)長(zhǎng)', duration),
758             (u'狀態(tài)', status),
759         ]
760 
761     def generateReport(self, test, result):
762         report_attrs = self.getReportAttributes(result)
763         generator = 'HTMLTestRunner %s' % __version__
764         stylesheet = self._generate_stylesheet()
765         heading = self._generate_heading(report_attrs)
766         report = self._generate_report(result)
767         ending = self._generate_ending()
768         chart = self._generate_chart(result)
769         output = self.HTML_TMPL % dict(
770             title = saxutils.escape(self.title),
771             generator = generator,
772             stylesheet = stylesheet,
773             heading = heading,
774             report = report,
775             ending = ending,
776             chart_script = chart
777         )
778         self.stream.write(output.encode('utf8'))
779 
780     def _generate_stylesheet(self):
781         return self.STYLESHEET_TMPL
782 
783     def _generate_heading(self, report_attrs):
784         a_lines = []
785         for name, value in report_attrs:
786             line = self.HEADING_ATTRIBUTE_TMPL % dict(
787                 name = saxutils.escape(name),
788                 value = saxutils.escape(value),
789             )
790             a_lines.append(line)
791         heading = self.HEADING_TMPL % dict(
792             title = saxutils.escape(self.title),
793             parameters = ''.join(a_lines),
794             description = saxutils.escape(self.description),
795         )
796         return heading
797 
798     def _generate_report(self, result):
799         rows = []
800         sortedResult = self.sortResult(result.result)
801         for cid, (cls, cls_results) in enumerate(sortedResult):
802             # subtotal for a class
803             np = nf = ne = 0
804             for n,t,o,e in cls_results:
805                 if n == 0: np += 1
806                 elif n == 1: nf += 1
807                 else: ne += 1
808 
809             # format class description
810             if cls.__module__ == "__main__":
811                 name = cls.__name__
812             else:
813                 name = "%s.%s" % (cls.__module__, cls.__name__)
814             doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
815             desc = doc and '%s: %s' % (name, doc) or name
816 
817             row = self.REPORT_CLASS_TMPL % dict(
818                 style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
819                 desc = desc,
820                 count = np+nf+ne,
821                 Pass = np,
822                 fail = nf,
823                 error = ne,
824                 cid = 'c%s' % (cid+1),
825             )
826             rows.append(row)
827 
828             for tid, (n,t,o,e) in enumerate(cls_results):
829                 self._generate_report_test(rows, cid, tid, n, t, o, e)
830 
831         report = self.REPORT_TMPL % dict(
832             test_list = ''.join(rows),
833             count = str(result.success_count+result.failure_count+result.error_count),
834             Pass = str(result.success_count),
835             fail = str(result.failure_count),
836             error = str(result.error_count),
837         )
838         return report
839 
840     def _generate_chart(self, result):
841         chart = self.ECHARTS_SCRIPT % dict(
842             Pass=str(result.success_count),
843             fail=str(result.failure_count),
844             error=str(result.error_count),
845         )
846         return chart
847 
848     def _generate_report_test(self, rows, cid, tid, n, t, o, e):
849         # e.g. 'pt1.1', 'ft1.1', etc
850         has_output = bool(o or e)
851         tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
852         name = t.id().split('.')[-1]
853         doc = t.shortDescription() or ""
854         desc = doc and ('%s: %s' % (name, doc)) or name
855         tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
856 
857         script = self.REPORT_TEST_OUTPUT_TMPL % dict(
858             id=tid,
859             output=saxutils.escape(o+e),
860         )
861 
862         row = tmpl % dict(
863             tid=tid,
864             Class=(n == 0 and 'hiddenRow' or 'none'),
865             style=(n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none')),
866             desc=desc,
867             script=script,
868             status=self.STATUS[n],
869         )
870         rows.append(row)
871         if not has_output:
872             return
873 
874     def _generate_ending(self):
875         return self.ENDING_TMPL
876 
877 
878 ##############################################################################
879 # Facilities for running tests from the command line
880 ##############################################################################
881 
882 # Note: Reuse unittest.TestProgram to launch test. In the future we may
883 # build our own launcher to support more specific command line
884 # parameters like test title, CSS, etc.
885 class TestProgram(unittest.TestProgram):
886     """
887     A variation of the unittest.TestProgram. Please refer to the base
888     class for command line parameters.
889     """
890     def runTests(self):
891         # Pick HTMLTestRunner as the default test runner.
892         # base class's testRunner parameter is not useful because it means
893         # we have to instantiate HTMLTestRunner before we know self.verbosity.
894         if self.testRunner is None:
895             self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
896         unittest.TestProgram.runTests(self)
897 
898 main = TestProgram
899 
900 ##############################################################################
901 # Executing this module from the command line
902 ##############################################################################
903 
904 if __name__ == "__main__":
905     main(module=None)
View Code

發(fā)送到郵箱郵件

python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具
python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具
 1 # -*- coding: utf-8 -*-
 2 import smtplib
 3 import zipfile
 4 from email.mime.text import MIMEText  #發(fā)送純文本信息
 5 from email.mime.multipart import MIMEMultipart  #發(fā)送帶附件的信息
 6 from email.header import Header  #導(dǎo)入配置庫(kù)
 7 from config.config import basedir
 8 import sys
 9 from config import config
10 import os
11 
12 # 1、壓縮文件
13 def cr_zip(outputName, inputPath):
14     # 將inputPath路徑下的文件壓縮成名字為outputName的文件,放到outputpath目錄下
15 
16     outputpath = inputPath + outputName
17     filelist = []
18     isfp = os.path.basename(inputPath)
19     if isfp:
20         print('%s is not path' % inputPath)
21         sys.exit(0)
22     else:
23         for root, subdirs, files in os.walk(inputPath):
24             for file in files:
25                 filelist.append(os.path.join(root, file))
26 
27     # 參數(shù)1,壓縮后的文件夾路徑加名字(如果只加name的話,會(huì)壓縮到調(diào)用這個(gè)方法的時(shí)候的文件路徑下);
28     # 參數(shù)2,'r' -----  打開一個(gè)存在的只讀ZIP文件'w' -----  清空并打開一個(gè)只寫的zip文件,或創(chuàng)建一個(gè)只寫的ZIP文件'a' -----  表示打開一個(gè)文件,并添加內(nèi)容
29     # 參數(shù)3,壓縮格式 ,可選的壓縮格式只有2個(gè):ZIP_STORE、ZIP_DEFLATED。ZIP_STORE是默認(rèn)的,表示不壓縮。ZIP_DEFLATED表示壓縮
30     zf = zipfile.ZipFile(outputpath, 'w', zipfile.ZIP_DEFLATED)
31     for f in filelist:
32         zf.write(f)
33     zf.close()
34 
35 # 2、發(fā)送郵件
36 def send_mail_report(title):
37     """1、獲取測(cè)試報(bào)告郵件服務(wù)器、發(fā)件人、收件人、發(fā)件人賬號(hào)密碼等信息"""
38     sender = config.sender   #發(fā)件人
39     receiver = config.receiver  #收件人
40     #第三方SMTP服務(wù)
41     server = config.server   #設(shè)置服務(wù)器
42     username = config.emailusername  #用戶名
43     password = config.emailpassword  #口令
44     port = config.smtp_server_port
45 
46     """2、獲取最新測(cè)試報(bào)告"""
47     reportPath=config.basedir+"/report/"
48     newReport = ""
49     for root, subdirs, files in os.walk(reportPath):
50         for file in files:
51             if os.path.splitext(file)[1] == ".html":  # 判斷該目錄下的文件擴(kuò)展名是否為html
52                 newReport=file
53 
54     """2.1調(diào)用cr_zip()方法,將測(cè)試報(bào)告壓縮一下。"""
55     cr_zip('TestReport.zip', basedir + '/report/')
56 
57     """3、生成郵件的內(nèi)容"""
58     msg = MIMEMultipart()      #MIMEMultipart(),創(chuàng)建一個(gè)帶附件的實(shí)例
59     msg["subject"] = title      #"""郵件需要三個(gè)頭部信息: From, To, 和 Subject"""
60     msg["from"] = Header(config.sender,'utf-8')
61     msg["to"] = Header(",".join(config.receiver),'utf-8')
62     with open(os.path.join(reportPath,newReport), 'rb') as f:
63         mailbody = f.read()
64     html = MIMEText(mailbody, _subtype='html', _charset='utf-8')
65     msg.attach(html)
66 
67     """4、將測(cè)試報(bào)告壓縮文件添加到郵件附件"""
68 
69     att = MIMEText(open(basedir + '/report/' + 'TestReport.zip', 'rb').read(), 'base64', 'utf-8')
70     att["Content-Type"] = 'application/octet-stream'   #application/octet-stream : 二進(jìn)制流數(shù)據(jù)(如常見(jiàn)的文件下載)
71     att.add_header("Content-Disposition", "attachment", filename="TestReport.zip")  #filename為附件名
72     msg.attach(att)
73 
74     """5、發(fā)送郵件"""
75     """5、發(fā)送郵件"""
76     try:
77         s = smtplib.SMTP(server, port)  # port=25 為 SMTP 端口號(hào)
78         s.ehlo()  # 向Gamil發(fā)送SMTP 'ehlo' 命令
79         s.starttls()
80         """s.set_debuglevel(1)認(rèn)證"""
81         s.login(username, password)
82         """發(fā)送郵件"""
83         s.sendmail(sender, receiver, msg.as_string())
84         s.quit()
85         print("郵件發(fā)送成功")
86     except smtplib.SMTPException as e:
87         print(e)
88         print("Error :無(wú)法發(fā)送郵件")
89 
90 
91 if __name__ =='__main__':
92     print(basedir+'/report/')
93     send_mail_report("接口測(cè)試報(bào)告")
View Code

日志文件文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-611799.html

python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具
python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu),python,selenium,開發(fā)語(yǔ)言,pycharm,測(cè)試工具
 1 # coding=utf-8
 2 import logging
 3 import time
 4 import os
 5 from config.config import basedir
 6 # proj_path = os.path.dirname(os.path.dirname(__file__))
 7 
 8 
 9 # os.path.join(a,b)注:第一個(gè)絕對(duì)路徑之前的參數(shù)將被忽略
10 
11 log_path = basedir +"/log/"
12 logname = os.path.join(log_path, '{0}.log'.format(time.strftime('%Y-%m-%d--%H_%M_%S')))
13 
14 class Log:
15     def __printconsole(self, level, message):
16         """創(chuàng)建一個(gè)logger"""
17         logger = logging.getLogger()
18         logger.setLevel(logging.DEBUG)
19         """創(chuàng)建一個(gè)handler,用于寫入日志文件"""
20         fh = logging.FileHandler(logname, 'a', encoding='utf-8')
21         fh.setLevel(logging.DEBUG)
22         """再創(chuàng)建一個(gè)handler,用于輸出到控制臺(tái)"""
23         ch = logging.StreamHandler()
24         ch.setLevel(logging.DEBUG)
25         """定義handler的輸出格式"""
26         formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
27         fh.setFormatter(formatter)
28         ch.setFormatter(formatter)
29         """給logger添加handler"""
30         logger.addHandler(fh)
31         logger.addHandler(ch)
32         """記錄一條日志"""
33         if level == 'info':
34             logger.info(message)
35         elif level == 'debug':
36             logger.debug(message)
37         elif level == 'warning':
38             logger.warning(message)
39         elif level == 'error':
40             logger.error(message)
41         logger.removeHandler(ch)
42         logger.removeHandler(fh)
43         """關(guān)閉打開的文件"""
44         fh.close()
45 
46     def debug(self, message):
47         self.__printconsole('debug', message)
48 
49     def info(self, message):
50         self.__printconsole('info', message)
51 
52     def warning(self, message):
53         self.__printconsole('warning', message)
54 
55     def error(self, message):
56         self.__printconsole('error', message)
57 
58 logger = Log()
View Code

到了這里,關(guān)于python+selenium-【六】-完整的項(xiàng)目結(jié)構(gòu)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【python】Flask網(wǎng)頁(yè)開發(fā)——論壇項(xiàng)目實(shí)戰(zhàn)(完整代碼)

    【python】Flask網(wǎng)頁(yè)開發(fā)——論壇項(xiàng)目實(shí)戰(zhàn)(完整代碼)

    筆記為自我總結(jié)整理的學(xué)習(xí)筆記,若有錯(cuò)誤歡迎指出喲~ 【論壇項(xiàng)目實(shí)戰(zhàn)】 【python】Flask網(wǎng)頁(yè)開發(fā)——論壇項(xiàng)目實(shí)戰(zhàn)(完整代碼) 【python】Flask網(wǎng)頁(yè)開發(fā)——論壇項(xiàng)目實(shí)戰(zhàn)(1.導(dǎo)航條實(shí)現(xiàn)) 【python】Flask網(wǎng)頁(yè)開發(fā)——論壇項(xiàng)目實(shí)戰(zhàn)(2.登錄與注冊(cè)) 【python】Flask網(wǎng)頁(yè)開發(fā)——論

    2024年04月28日
    瀏覽(23)
  • Web開發(fā)自動(dòng)測(cè)試工具-Selenium的具體使用辦法(填坑中……)

    https://juejin.cn/post/7074779332819812389 Selenium 是最廣泛使用的開源 Web UI(用戶界面)自動(dòng)化測(cè)試套件之一 。 Selenium 支持的語(yǔ)言包括C#,Java,Perl,PHP,Python 和 Ruby。目前,Selenium Web 驅(qū)動(dòng)程序最受 Python 和 C#歡迎。 Selenium 測(cè)試腳本可以使用任何支持的編程語(yǔ)言進(jìn)行編碼,并且可以

    2024年02月02日
    瀏覽(19)
  • python自動(dòng)化測(cè)試工具selenium

    selenium 是網(wǎng)頁(yè)應(yīng)用中最流行的自動(dòng)化測(cè)試工具,可以用來(lái)做自動(dòng)化測(cè)試或者瀏覽器爬蟲等。官網(wǎng)地址為:Selenium。相對(duì)于另外一款web自動(dòng)化測(cè)試工具QTP來(lái)說(shuō)有如下優(yōu)點(diǎn): 免費(fèi)開源輕量級(jí),不同語(yǔ)言只需要一個(gè)體積很小的依賴包 支持多種系統(tǒng),包括Windows,Mac,Linux 支持多種瀏

    2024年02月08日
    瀏覽(26)
  • Python自動(dòng)化測(cè)試工具selenium使用指南

    Python自動(dòng)化測(cè)試工具selenium使用指南

    概述 selenium 是網(wǎng)頁(yè)應(yīng)用中最流行的自動(dòng)化測(cè)試工具,可以用來(lái)做自動(dòng)化測(cè)試或者瀏覽器爬蟲等。官網(wǎng)地址為:相對(duì)于另外一款web自動(dòng)化測(cè)試工具QTP來(lái)說(shuō)有如下優(yōu)點(diǎn): 免費(fèi)開源輕量級(jí),不同語(yǔ)言只需要一個(gè)體積很小的依賴包 支持多種系統(tǒng),包括Windows,Mac,Linux 支持多種瀏覽器

    2024年02月04日
    瀏覽(80)
  • 如何使用Python自動(dòng)化測(cè)試工具Selenium進(jìn)行網(wǎng)頁(yè)自動(dòng)化?

    如何使用Python自動(dòng)化測(cè)試工具Selenium進(jìn)行網(wǎng)頁(yè)自動(dòng)化?

    Selenium 是一個(gè)流行的Web自動(dòng)化測(cè)試框架, 它支持多種編程語(yǔ)言和瀏覽器,并提供了豐富的API和工具來(lái)模擬用戶在瀏覽器中的行為 。 Selenium可以通過(guò)代碼驅(qū)動(dòng)瀏覽器自動(dòng)化測(cè)試流程,包括頁(yè)面導(dǎo)航、元素查找、數(shù)據(jù)填充、點(diǎn)擊操作等。 與PyAutoGUI和AutoIt相比, Selenium更適合于處

    2023年04月09日
    瀏覽(111)
  • 測(cè)試員進(jìn)階必看系列 “ python自動(dòng)化測(cè)試工具selenium使用指南 ”

    測(cè)試員進(jìn)階必看系列 “ python自動(dòng)化測(cè)試工具selenium使用指南 ”

    概述 python+selenium環(huán)境安裝 使用selenium啟動(dòng)瀏覽器 selenium頁(yè)面加載等待和檢測(cè) 使用time.sleep()等待 使用implicitly_wait設(shè)置最長(zhǎng)等待時(shí)間 使用WebDriverWait設(shè)置等待條件 檢測(cè)document是否加載完成 selenium元素定位和讀取 查找元素 dom元素交互 查找元素失敗處理 selenium交互控制 ActionChains動(dòng)

    2024年02月05日
    瀏覽(117)
  • Python Selenium4.3.0(新語(yǔ)法) web自動(dòng)化測(cè)試工具

    1 介紹 Selenium是一個(gè)用于Web應(yīng)用程序測(cè)試的工具。Selenium測(cè)試直接運(yùn)行在瀏覽器中,就像真正的用戶在操作一樣。 支持的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera,Edge等 這個(gè)工具的主要功能包括:測(cè)試與瀏覽器的兼容性——測(cè)試應(yīng)用程序看是否能夠

    2024年01月24日
    瀏覽(33)
  • python自動(dòng)化測(cè)試工具selenium使用指南 ,絕對(duì)能幫到你

    python自動(dòng)化測(cè)試工具selenium使用指南 ,絕對(duì)能幫到你

    目錄 概述 python+selenium環(huán)境安裝 使用selenium啟動(dòng)瀏覽器 selenium頁(yè)面加載等待和檢測(cè) 使用time.sleep()等待 使用implicitly_wait設(shè)置最長(zhǎng)等待時(shí)間 使用WebDriverWait設(shè)置等待條件 檢測(cè)document是否加載完成 selenium元素定位和讀取 查找元素 dom元素交互 查找元素失敗處理 selenium交互控制 Actio

    2024年02月08日
    瀏覽(49)
  • ubuntu 18.04 配置自動(dòng)化測(cè)試工具 appium + selenium+python3

    sudo add-apt-repository ppa:danielrichter2007/grub-customizer sudo apt-get update sudo apt-get install grub-customizer sudo apt-get install openjdk-8-jdk ================================================================================ 【已經(jīng)安裝Android studio 可以跳過(guò)】 下載SDK =============================== 配置環(huán)境變量 【配置環(huán)境變量

    2024年02月03日
    瀏覽(27)
  • Python開發(fā)中自動(dòng)化構(gòu)建項(xiàng)目結(jié)構(gòu)樣式

    摘要: 在項(xiàng)目開發(fā)過(guò)程中,一個(gè)良好的項(xiàng)目結(jié)構(gòu)對(duì)于團(tuán)隊(duì)的協(xié)作和代碼的可維護(hù)性起著重要作用。通過(guò)使用自動(dòng)生成項(xiàng)目結(jié)構(gòu)文字樣式的工具。不僅節(jié)省了手動(dòng)編寫項(xiàng)目結(jié)構(gòu)的麻煩,還確保了結(jié)構(gòu)的一致性和準(zhǔn)確性。 本文分享自華為云社區(qū)《【Python】自動(dòng)化構(gòu)建項(xiàng)目結(jié)構(gòu)樣

    2024年02月11日
    瀏覽(20)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包