Web端自动化测试--Python+selenium+unittest

Page Object 是 Selenium 自动化测试项目开发实践的最佳设计模式之一,Page Object 的主要体现于对界面交互细节的封装,这样可以使测试案例更关注与业务而非界面细节,提高测试案例的可读性。

认识 Paget Object

一、Page object 简称POM框架

  • POM提供了一种在UI层操作、业务流程与验证分离的模式,这使得测试代码变得更加清晰和高可读性;
  • 对象库与用例分离,使得我们更好的复用对象,甚至能与不同的工具进行深度结合应用;
  • 可复用的页面方法代码会变得更加优化;
  • 更加有效的命名方式使得我们更加清晰的知道方法所操作的UI元素。例如我们要回到首页,方法名命名为: gotoHomePage(),通过方法名即可清晰的知道具体的功能实现。;

二、Page object设计模式的优点

  1. 减少代码的重复;
  2. 提高测试用例的可读性;
  3. 提高测试用例的可维护性,特别是针对 UI 频繁变化的项目;

实战--博客园登录自动化测试发送测试报告

思路分析:

  • 浏览器封装;
  • 页面截图函数封装,偏于后续浏览查看;
  • 页面元素基础类操作封装,包含断言设计;
  • 登录元素操作封装;
  • 登录测试用例设计;

代码参考

myunit.py

# 浏览器驱动封装
'''
@author: Yvon_早安阳光
'''
from selenium import webdriver
import unittest 
class MyTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Firefox()  
    def tearDown(self):
        self.driver.quit()

function.py

'''
@author: Yvon_早安阳光
'''
import time
from selenium import webdriver
 
#封装截图函数
def insert_img(driver,filename):
    file_path = 'insert_img/'
    now = time.strftime("%Y-%m-%d %H_%M_%S")
    screen_name = file_path + '\\' + now + filename
    driver.get_screenshot_as_file(screen_name)
     
# 登录成功截图
def success_img(driver,file_name):
    filepath = 'success_img/'
    now = time.strftime("%Y-%m-%d %H_%M_%S")
    screenname = filepath + '\\' + now + file_name
    driver.get_screenshot_as_file(screenname)    
        
if __name__ == '__main__':
    driver = webdriver.Firefox()
    driver.quit()

Base.py

# 页面元素基础类操作封装
'''
@author: Yvon_早安阳光
'''
class Page(object):

  #BasePage封装所有页面都公用的方法,例如driver, url ,FindElement等
    login_url = "https://passport.cnblogs.com/user/signin"
    #实例化BasePage类时,最先执行的就是__init__方法,该方法的入参,其实就是BasePage类的入参。
    #__init__方法不能有返回值,只能返回None,self只实例本身,相较于类Page而言

    def __init__(self,selenium_driver,base_url = login_url,parent = None):
        self.base_url = base_url
        self.driver = selenium_driver
        self.timeout =10
        self.parent = parent

    '''通过title断言进入的页面是否正确。使用title获取当前窗口title,检查输入的title是否在当前title中,返回比较结果(True 或 False)'''  

    def on_page(self):
        return self.driver.current_url == (self.base_url + self.url)

    '''#打开页面,并校验页面链接是否加载正确,以单下划线_开头的方法,在使用import *时,该方法不会被导入,保证该方法为类私有的。'''

    def _open(self,url):
        #使用get打开访问链接地址
        url = self.base_url + url
        self.driver.get(url)
        #使用assert进行校验,打开的窗口title是否与配置的title一致。调用on_page()方法
       # assert self.on_page(), u"打开开页面失败 %s"%url
      '''定义open方法,调用_open()进行打开链接'''

    def open(self):
        self._open(self.url)

    def find_elements(self,*loc):
        return self.driver.find_elements(*loc)

    '''重写switch_frame方法'''
    def switch_frame(self, loc):
        return self.driver.switch_to_frame(loc)

    '''script方法,用于执行js脚本,范围执行结果'''
    def script(self, src):
        self.driver.execute_script(src)

    '''重写定义send_keys方法'''
    def send_keys(self, loc, vaule, clear_first=True, click_first=True):
        try:
            loc = getattr(self,"_%s"% loc)  #getattr相当于实现self.loc
            if click_first:
                self.find_element(*loc).click()
            if clear_first:
                self.find_element(*loc).clear()
                self.find_element(*loc).send_keys(vaule)         
        except AttributeError:
            print (u"%s 页面中未能找到 %s 元素"%(self, loc))

LoginPage.py

登录元素操作封装
'''
@author: Yvon_早安阳光
'''
from selenium.webdriver.common.by import By
from Base import Page
from time import sleep

class login(Page):
    '''博客园登录页面模型'''
    url = '/'
    # 定位器
    '''用户名'''
    username_loc = (By.ID,"input1")
    '''密码'''
    password_loc = (By.ID,"input2")
    '''登陆按钮'''
    submit_loc = (By.ID,"signin")

  '''输入用户名:调用send_keys对象,输入用户名'''
    def input_username(self,username):
        self.find_element(*self.username_loc).clear()
        self.find_element(*self.username_loc).send_keys(username)        
             
 '''输入密码:调用send_keys对象,输入密码'''
    def input_password(self,password):
        self.find_element(*self.password_loc).clear()
        self.find_element(*self.password_loc).send_keys(password)
 
  '''点击登录:调用send_keys对象,点击登录''' 
    def click_submit(self):
        self.find_element(*self.submit_loc).click()
         
   # 定义统一登录入口
    def user_login(self,username = '123',password = '123'):
        '''获取用户的用户名和密码'''
        self.open()
        self.input_username(username)
        self.input_password(password)
        self.click_submit()
        sleep(2)
         
    '''用户为空'''
    user_error_hint_loc = (By.XPATH,".//*[@id='tip_input1']")
    '''密码为空'''
    pawd_error_hint_loc = (By.XPATH,".//*[@id='tip_input2']")
    '''用户名和密码不匹配'''
    user_pawd_error_hint_loc = (By.XPATH,".//*[@id='tip_btn']")
    '''登陆成功ID校验'''
    user_login_success_loc = (By.ID,"lnk_current_user")
   
    #用户名错误提示
    def user_error_hint(self):
        return self.find_element(*self.user_error_hint_loc).text
     
    #密码错误提示
    def pawd_error_hint(self):
        return self.find_element(*self.pawd_error_hint_loc).text
     
    #用户名和密码不匹配错误提示
    def user_pawd_error_hint(self):
        return self.find_element(*self.user_pawd_error_hint_loc).text
    #登录成功用户名
    def user_login_success(self):
        return self.find_element(*self.user_login_success_loc).text

Login_sta.py

登录测试用例
'''
@author: Yvon_早安阳光
'''
from time import sleep
import unittest,random
from Login1 import myunit
from LoginPage import login
from Login1 import function

class LoginTest(myunit.MyTest):
    '''博客园登录'''
     
    #测试用户登录
    def user_login_verify(self,username = "",password = ""): 
        login(self.driver).user_login(username,password)
         
    def test_login1(self):
        '''用户名、密码为空'''
        self.user_login_verify()
        po = login(self.driver)
        self.assertEqual(po.user_error_hint(),'请输入登录用户名')    
        print(po.user_error_hint())
        sleep(1)
#         '''单个截图'''
#         screen_name = 'insert_img/' + '\\' + time.strftime("%Y-%m-%d %H_%M_%S")
#         self.driver.get_screenshot_as_file(str(screen_name ) + "请输入登录用户名.jpg")
        u"""封装函数截图"""
        function.insert_img(self.driver,"用户名和密码为空.jpg")
  
    def test_login2(self):
        '''用户名正确、密码为空'''
        self.user_login_verify(username='a2036')
        po = login(self.driver)
        self.assertEqual(po.pawd_error_hint(),'请输入密码')       
        print(po.pawd_error_hint())
        sleep(1)
        function.insert_img(self.driver,"用户名正确-密码为空.jpg") 
                       
    def test_login3(self):
        '''用户名为空、密码正确'''
        self.user_login_verify(password ='123456')
        po = login(self.driver)
        self.assertEqual(po.user_error_hint(),'请输入登录用户名')      
        print(po.user_error_hint())
        sleep(1)
        function.insert_img(self.driver,"用户名为空-密码正确.jpg")
                
    def test_login4(self):
        '''用户名和密码不匹配'''
        character = random.choice('zyxwvutsrqponmlkjihgfedcba')
        user_name = 'zhangsan' + character
        self.user_login_verify(username=user_name,password ='123456')
        sleep(2)
        po = login(self.driver)
        self.assertEqual(po.user_pawd_error_hint(),'该用户不存在\n\n联系 contact@cnblogs.com')
        print(po.user_pawd_error_hint())
        sleep(1)
        function.insert_img(self.driver,"用户名和密码不匹配.jpg")
          
    def test_login5(self):
        '''用户名、密码正确登录'''
        self.user_login_verify(username='a2036', password ='20A3d4p43W36#20')
        sleep(1)
        po = login(self.driver)
        sleep(1)
        self.assertEqual(po.user_login_success(),'a2036')
        print(po.user_login_success())
        function.success_img(self.driver,"登录成功.jpg")
         
if __name__ == '__main__':
    unittest.main()

Send_mail_report.py

发送邮件包含测试报告、邮件标题、邮件正文,附件(图片、PDF)
'''
@author: Yvon_早安阳光
'''
import unittest,time,os,smtplib
from HTMLTestRunner import HTMLTestRunner
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
#======================定义发送邮件======================
def send_mail(file_new):
    #测试报告
    '''file_new是指最新的测试报告'''
    f = open(file_new,'rb')
    mail_body = f.read()
    f.close()
     
    msg = MIMEMultipart()
    '''邮件标题'''
    msg['Subject'] = Header('博客园登录测试报告' ,'utf-8')
     
    '''邮件正文'''
    zhengwen = "各位,详情测试结果请见附件文档"
    msg.attach(MIMEText(zhengwen, 'plain')) 
     
    '''附件测试报告 '''
    att= MIMEText(mail_body,'html','utf-8')
    att.add_header('Content-Disposition', 'attachment', filename='New_TestReport.html')
    msg.attach(att)
     
    '''附件图片'''
    jpgpart = MIMEApplication(open('fujian/测试计划.jpg','rb').read())
    jpgpart.add_header('Content-Disposition', 'attachment', filename='测试计划.jpg')
    msg.attach(jpgpart)
     
    '''附件PDF'''
    pdfpart = MIMEApplication(open('fujian/功能需求.pdf','rb').read())
    pdfpart.add_header('Content-Disposition', 'attachment', filename='功能需求.pdf')
    msg.attach(pdfpart)
 
    #发送邮件
    smtp = smtplib.SMTP()
    smtp.connect('smtp.exmail.qq.com')
    smtp.login('fajin@bertadata.com','36#20qa20A')
    smtp.sendmail('fajin@bertadata.com','huiselanse@yeah.net',msg.as_string())
    print('email has send out !')
    smtp.quit()
#======================查找测试报告的目录,找到最新生成的测试报告======================
def new_report(testreport):
    lists = os.listdir(testreport)
    lists.sort(key = lambda fn: os.path.getatime(testreport + '\\' + fn))
    file_new = os.path.join(testreport,lists[-1])
    print(file_new)
    return file_new
 
if __name__ == '__main__':
    #指定测试用例为当前文件夹下的目录
    test_dir = './'
    discover = unittest.defaultTestLoader.discover(test_dir, pattern='login_sta*.py')
    
    #定义报告存放的路径
    result_report_dir= 'result_report/'
    '''实时当前的时间'''
    now = time.strftime("%Y-%m-%d %H_%M_%S")
    file_name = result_report_dir + '\\' + now +'result.html'
    fp = open(file_name,'wb')    
    #定义测试报告
    runner =HTMLTestRunner(stream = fp,title ='自动化测试报告',
                           description='用例执行情况:')
    runner.run(discover)
    fp.close() #关闭报告文件
     
    new_report = new_report(result_report_dir)
    send_mail(new_report)

脚本目录

1.本地文件目录.png

2.图片和文档附件存放目录.png

3.测试用例测试结果截图目录.png

4.测试报告目录.png

5.登录成功附件目录.png

6.邮件接收附件和测试报告.png

7.自动化详细测试报告.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容