前言:
从前面五篇,对selenium大概也有了个理解,如何定位元素,如何操作元素,如何切换句柄。这时候就要做个小demo实战下了!
功能主要有:
1-PO模式,设计page、testcase、testsuit
2-打印log文件
3-生成测试报告
4-压缩测试报告,发送到邮箱
一、项目结构
二、pages讲解
pages是对要写自动化的页面进行分离,抽取出来定位元素,执行方法。以login为例子
打开页面--->进入登录页面--->选择账号登录---->输入框输入用户名密码--->点击登录
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 : 登录页面
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 '''用户登录页面'''
17 # 定位器,通过元素属性定位元素对象
18 login_loc = (By.XPATH, "//a[@class='header_link console']") #首页控制台链接
19 account_loc = (By.XPATH, "//div[@class='ant-tabs-nav ant-tabs-nav-animated']//div[2]//span") #账号登录tab选项
20 username_loc = (By.XPATH, "//input[@id='username_or_email']") #用户名/邮箱/手机号input
21 password_loc = (By.XPATH, "//input[@id='password']")
22 submit_loc = (By.XPATH, "//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" # 登录状态的url
26
27 """打开官网首页"""
28 def openPage(self):
29 self.open(baseurl_guanwang, title_guanwang)
30
31 """进入登录页面"""
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地址不对! %s" % self.login_url
36
37 """选择账号登录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 # 点击登录
54 def click_submit(self):
55 self.find_element(*self.submit_loc).click()
56
57 #统一登录入口,方便其他地方调用
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 # 在这里得到当前窗口句柄
64 allhandles = self.driver.window_handles # 获取所有窗口句柄
65 for handle in allhandles: # 在所有窗口中查找弹出窗口
66 if handle != nowhandle:
67 self.driver.switch_to.window(handle) # 这两步是在弹出窗口中进行的操作,证明我们确实进入了
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 # 登录成功后比对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地址不对! %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就是对pages对应的方法组装,进行case设计,因为login页面没写太多case,就以一个为例子
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 : 登录校验
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 """控制台登录"""
18
19 @classmethod
20 def setUpClass(cls):
21 logger.info("##########登录测试 开始##############")
22 cls.driver = webdriver.Chrome()
23 cls.username = username
24 cls.password = password
25 cls.loginPage = LoginPage(cls.driver)
26
27 # 用例执行体
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("##########登录测试 结束##############")
36
37
38 if __name__ == "__main__":
39 unittest.main()
四、testsuite讲解
testsuite是对testcase进行组装,统一的一个执行入口。testsuite有多种组合方法,可以详细的去了解下,代码的注释中有几种的讲解
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 # 生成报告文件的参数
13 basedir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
14 print(basedir)
15 # discover这个方法可以递归的去识别路径下所有符合pattern的文件,将这些文件加入套件
16 suite = unittest.defaultTestLoader.discover(basedir+'/test/testcase/', pattern='*.py')
17
18 # 1、用TestSuite的addTests()方法,将测试用例组装成测试套件,不用TestLoader时,传入的是case里面的方法名。用TestLoader时传入类的类名
19 # addTest传入单个的Testcase方法,addTests传入Testcase方法数组
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传入测试用例,但是TestLoader无法对case排序
25
26 # 2.1 loadTestsFromName传入‘模块名.TestCase名’(文件名.里面的类名)
27 # suite.addTests(unittest.TestLoader().loadTestsFromName('test_mathfunc.TestMathFunc'))
28 # # 2.2 loadTestsFromNames就是传入列表即有多个testcase时,依次传入文件
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 # 运行suite,这样写是将结果输出到控制台
37 # verbosity参数可以控制输出的错误报告的详细程度,默认是1,
38 # 如果设为0,则不输出每一用例的执行结果,即没有上面的结果中的第1行;
39 # 如果设为2,则输出详细的执行结果
40 # runner = unittest.TextTestRunner(verbosity=2)
41 # runner.run(suite)
42
43 #运行suite,并将结果输出到Html报告里
44 #生成报告文件的参数
45 report_title = 'facepp-UI自动化测试'
46 desc = '饼图统计测试执行情况'
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 # 发送报告到邮箱
54 time.sleep(1)
55 Send_email.cr_zip('TestReport.zip',basedir + '/report/')
56 Send_email.send_mail_report("facepp-UI自动化测试!!!")
五、生成html报告
忽略我的报错,因为我现在的电脑上没有安装浏览器的驱动
大概就是这个样子,饼图会将通过的失败的错误的。下面的表格会展示出所有的case,详情里会将错误信息展示出来
六、发送到邮箱
执行成功后,会发送到邮箱一封邮件,发送人、收件人都是可以设置的。邮件内容为测试结果,html报告打包后的压缩文件,可以下载后,打开查看html报告
1 # 发送报告到邮箱
2 time.sleep(1)
3 Send_email.cr_zip('TestReport.zip',basedir + '/report/')
4 Send_email.send_mail_report("facepp-UI自动化测试!!!")
七、生成日志
日志格式如上,年月日时分秒,然后info、error等信息
使用时,引入logger类,然后logger.info("##########登录测试 开始##############")即可
八、生成html报告源码|发送到邮箱邮件源码 |日志源码
生成html报告
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 = ''
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添加执行情况统计图 (灰蓝)
75
76 Version 0.9.0
77 * 改成Python 3.x (灰蓝)
78
79 Version 0.8.3
80 * 使用 Bootstrap稍加美化 (灰蓝)
81 * 改为中文 (灰蓝)
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
205
206
207 %(stylesheet)s
208
209
210
211
305
306
307 %(heading)s
308 %(report)s
309 %(ending)s
310 %(chart_script)s
311
312
313