python webdriver 一步一步搭建数据驱动测试框架的过程和总结
Posted xiaxiaoxu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python webdriver 一步一步搭建数据驱动测试框架的过程和总结相关的知识,希望对你有一定的参考价值。
一步一步搭建数据驱动测试框架的过程和总结
数据驱动框架结构:
Action:
封装的操作元素的函数,如login,添加联系人。。。
conf:
日志配置文件
定位元素配置文件
数据库配置文件
PageObject:
一个页面是一个类,类的方法可以获取页面上的相关元素
ProjectVar:
工程路径
工程相关的全局变量
TestData:(文件或excel)
测试用例
测试数据
TestScript:
运行测试框架的主程序:入口,主要读取测试数据的文件
记录测试结果。
Util-工具类
功能:
读取配置文件
excel工具类
时间类
查找元素的方法
读取定位元素配置文件的方法
日志方法
日志操作
截图
报告模板
我的思路是从小到大的来继承这个框架,首先明确的是我要实现的功能是登录126邮箱,并添加联系人。
步骤1--如果我不用框架的方式,直接罗列代码的话,怎么实现呢,先来试着完成这个,
pycharm里新建一个工程-dataDrivenTestPractice1
在工程下新建一个TestScript的包
在TestScript包下新建一个TestScript的python文件
代码:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://mail.126.com‘)
try:
wait=WebDriverWait(driver,10,0.2)#显示等待
driver.switch_to.frame(driver.find_element_by_xpath("//iframe[@id=‘x-URS-iframe‘]"))#切换到用户名和密码输入框所在的frame元素
name=wait.until(lambda x:x.find_element_by_xpath("//input[@placeholder=‘邮箱帐号或手机号‘ and @name=‘email‘]"))
name.send_keys(‘xiaxiaoxu1987‘)
password=wait.until(lambda x:x.find_element_by_xpath("//input[@placeholder=‘密码‘]"))
password.send_keys(‘gloryroad‘)
submit=wait.until(lambda x:x.find_element_by_xpath("//a[@id=‘dologin‘]"))
submit.click()
driver.switch_to.default_content()#在pycharm里用switch_to_default_content()会被加删除线,out了
time.sleep(5)
assert u"退出" in driver.page_source,"no exist in page_source"
address_book_link=wait.until(lambda x:x.find_element_by_xpath("//div[text()=‘通讯录‘]"))
address_book_link.click()
#assert u"新建联系人" in driver.page_source
add_contact_button=wait.until(lambda x:x.find_element_by_xpath("//span[text()=‘新建联系人‘]"))
add_contact_button.click()
contact_name=wait.until(lambda x:x.find_element_by_xpath("//a[@title=‘编辑详细姓名‘]/preceding-sibling::div/input"))
contact_name.send_keys(u"徐凤钗")
contact_email=wait.until(lambda x:x.find_element_by_xpath("//*[@id=‘iaddress_MAIL_wrap‘]//input"))
contact_email.send_keys("[email protected]")
contact_is_star=wait.until(lambda x:x.find_element_by_xpath("//span[text()=‘设为星标联系人‘]/preceding-sibling::span/b"))
contact_is_star.click()
contact_mobile=wait.until(lambda x:x.find_element_by_xpath("//*[@id=‘iaddress_TEL_wrap‘]//dd//input"))
contact_mobile.send_keys(‘18141134488‘)
contact_other_info=wait.until(lambda x:x.find_element_by_xpath("//textarea"))
contact_other_info.send_keys(‘my wife‘)
contact_save_button=wait.until(lambda x:x.find_element_by_xpath("//span[.=‘确 定‘]"))
contact_save_button.click()
except TimeoutException, e:
# 捕获TimeoutException异常
print traceback.print_exc()
except NoSuchElementException, e:
# 捕获NoSuchElementException异常
print traceback.print_exc()
except Exception, e:
# 捕获其他异常
print traceback.print_exc()
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/TestScript.py
Process finished with exit code 0
至此,第一步现在已经实现了,接下来看一下如何把这个程序进行程序和数据的分离呢?
先看一下程序的主要功能:
登录邮箱-->打开联系人页面-->点击添加联系人按钮-->在弹窗中输入联系人的信息-->点击保存按钮。
还有数据驱动的核心-->数据和程序的分离
首先,登录邮箱的步骤可以抽离出来,进行独立的封装,暂定login模块
接下来就对login的功能做一个封装,然后在出程序里进行调用。
步骤2—把登录的功能抽离出来,进行封装,在主程序中调用
在工程下新建一个PageObject的包
在PageObject包下新建一个LoginPage的python文件,来封装login的操作
代码:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
class LoginPage(object):
def __init__(self,driver):
self.driver=driver
def login(self):
try:
wait = WebDriverWait(driver, 10, 0.2) # 显示等待
self.driver.switch_to.frame(
self.driver.find_element_by_xpath("//iframe[@id=‘x-URS-iframe‘]")) # 切换到用户名和密码输入框所在的frame元素
name = wait.until(lambda x: x.find_element_by_xpath("//input[@placeholder=‘邮箱帐号或手机号‘ and @name=‘email‘]"))
name.send_keys(‘xiaxiaoxu1987‘)
password = wait.until(lambda x: x.find_element_by_xpath("//input[@placeholder=‘密码‘]"))
password.send_keys(‘gloryroad‘)
submit = wait.until(lambda x: x.find_element_by_xpath("//a[@id=‘dologin‘]"))
submit.click()
self.driver.switch_to.default_content() # 在pycharm里用switch_to_default_content()会被加删除线,out了
time.sleep(5)
assert u"退出" in self.driver.page_source, "no exist in page_source"
except TimeoutException, e:
# 捕获TimeoutException异常
print traceback.print_exc()
except NoSuchElementException, e:
# 捕获NoSuchElementException异常
print traceback.print_exc()
except Exception, e:
# 捕获其他异常
print traceback.print_exc()
if __name__=="__main__":
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get("http:\mail.126.com")
login=LoginPage(driver)
login.login()
结果:登录成功
C:Python27python.exe D:/test/dataDrivenTestPractice1/PageObject/Login_page.py
Process finished with exit code 0
至此,login功能封装好了,下面在修改主程序,调用LoginPage
TestScript.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from PageObject.Login_Page import *
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://mail.126.com‘)
lp=LoginPage(driver)
wait = WebDriverWait(driver, 10, 0.2) # 显示等待
try:
lp.login()
address_book_link=wait.until(lambda x:x.find_element_by_xpath("//div[text()=‘通讯录‘]"))
address_book_link.click()
#assert u"新建联系人" in driver.page_source
add_contact_button=wait.until(lambda x:x.find_element_by_xpath("//span[text()=‘新建联系人‘]"))
add_contact_button.click()
contact_name=wait.until(lambda x:x.find_element_by_xpath("//a[@title=‘编辑详细姓名‘]/preceding-sibling::div/input"))
contact_name.send_keys(u"徐凤钗")
contact_email=wait.until(lambda x:x.find_element_by_xpath("//*[@id=‘iaddress_MAIL_wrap‘]//input"))
contact_email.send_keys("[email protected]")
contact_is_star=wait.until(lambda x:x.find_element_by_xpath("//span[text()=‘设为星标联系人‘]/preceding-sibling::span/b"))
contact_is_star.click()
contact_mobile=wait.until(lambda x:x.find_element_by_xpath("//*[@id=‘iaddress_TEL_wrap‘]//dd//input"))
contact_mobile.send_keys(‘18141134488‘)
contact_other_info=wait.until(lambda x:x.find_element_by_xpath("//textarea"))
contact_other_info.send_keys(‘my wife‘)
contact_save_button=wait.until(lambda x:x.find_element_by_xpath("//span[.=‘确 定‘]"))
contact_save_button.click()
except TimeoutException, e:
# 捕获TimeoutException异常
print traceback.print_exc()
except NoSuchElementException, e:
# 捕获NoSuchElementException异常
print traceback.print_exc()
except Exception, e:
# 捕获其他异常
print traceback.print_exc()
结果:成功
C:Python27python.exe D:/test/dataDrivenTestPractice1/TestScript/TestScript.py
Process finished with exit code 0
从主程序来看,添加联系人的动作也是可以进行封装的 ,先来进行这部分的封装,后续有问题再往回推导,先按照这个思路做一遍,然后再做一遍
步骤3—封装添加联系人的功能
在PageObject包下新建AddressBook的python文件
代码AddressBook.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from PageObject.Login_Page import *
class AddressBook(object):
def __init__(self,driver):
self.driver=driver
def add_contact(self):
try:
wait = WebDriverWait(self.driver, 10, 0.2) # 显示等待
address_book_link = wait.until(lambda x: x.find_element_by_xpath("//div[text()=‘通讯录‘]"))
address_book_link.click()
# assert u"新建联系人" in driver.page_source
add_contact_button = wait.until(lambda x: x.find_element_by_xpath("//span[text()=‘新建联系人‘]"))
add_contact_button.click()
contact_name = wait.until(
lambda x: x.find_element_by_xpath("//a[@title=‘编辑详细姓名‘]/preceding-sibling::div/input"))
contact_name.send_keys(u"徐凤钗")
contact_email = wait.until(lambda x: x.find_element_by_xpath("//*[@id=‘iaddress_MAIL_wrap‘]//input"))
contact_email.send_keys("[email protected]")
contact_is_star = wait.until(
lambda x: x.find_element_by_xpath("//span[text()=‘设为星标联系人‘]/preceding-sibling::span/b"))
contact_is_star.click()
contact_mobile = wait.until(lambda x: x.find_element_by_xpath("//*[@id=‘iaddress_TEL_wrap‘]//dd//input"))
contact_mobile.send_keys(‘18141134488‘)
contact_other_info = wait.until(lambda x: x.find_element_by_xpath("//textarea"))
contact_other_info.send_keys(‘my wife‘)
contact_save_button = wait.until(lambda x: x.find_element_by_xpath("//span[.=‘确 定‘]"))
contact_save_button.click()
except TimeoutException, e:
# 捕获TimeoutException异常
print traceback.print_exc()
except NoSuchElementException, e:
# 捕获NoSuchElementException异常
print traceback.print_exc()
except Exception, e:
# 捕获其他异常
print traceback.print_exc()
if __name__=="__main__":
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://mail.126.com‘)
lp=LoginPage(driver)
lp.login()
ab=AddressBook(driver)
ab.add_contact()
结果:成功
C:Python27python.exe D:/test/dataDrivenTestPractice1/PageObject/AddressBook.py
Process finished with exit code 0
至此,添加联系人功能粗略的封装完了,下面在主程序中调用
TestScript.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from PageObject.Login_Page import *
from PageObject.AddressBook import *
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://mail.126.com‘)
loginPage=LoginPage(driver)
addressBook=AddressBook(driver)
loginPage.login()
addressBook.add_contact()
结果:成功
C:Python27python.exe D:/test/dataDrivenTestPractice1/TestScript/TestScript.py
Process finished with exit code 0
至此,程序的两个功能-登录和添加联系人已经粗略的封装起来了,在主程序中可以直接调用函数就可以了,下面再回顾一下程序的主要功能:
登录邮箱-->打开联系人页面-->点击添加联系人按钮à在弹窗中输入联系人的信息-->点击保存按钮。还有数据驱动的核心-->数据和程序的分离
接下来,我想尝试把数据的部分抽离出来,下面来看下这个数据怎么分离呢?
从程序来看,调用数据的部分是在刚才封装的两个模块里,Login_Page和AddressBook,看下程序是咋用数据的
Login_page:
AddressBook:
在封装这两个函数时,没有留出参数,直接在函数里写死了,这显然是比较low的,那怎么弄呢?下面尝试把函数中需要调用的数据用参数代替,然后在调用的时候传进去
先来修改login函数,看看怎么改成用参数传,这时候本人突然懵逼了,这么多个不一样的数据,没办法用参数啊。。。
如果要用参数传需要把login函数进行拆分,可以看到login函数中有几个部分是可以进行封装起来的,比如找frame元素的功能,找到用户名输入框元素的功能,然后再找单个元素的函数中就可以传递数据给参数进行查找了
步骤4—封装查找元素的功能
修改LoginPage.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
class LoginPage(object):
def __init__(self,driver):
self.driver=driver
self.wait = WebDriverWait(self.driver, 10, 0.2) # 显示等待
def getFrame(self,locateType,locateExpression):
frame=self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression))#"//iframe[@id=‘x-URS-iframe‘]"
return frame
def getUserName(self,locateType,locateExpression):
userName=self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression))#"//input[@placeholder=‘邮箱帐号或手机号‘ and @name=‘email‘]"
return userName
def getPassword(self,locateType,locateExpression):
password=self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression))#"//input[@placeholder=‘密码‘]"
return password
def getLoginButton(self,locateType,locateExpression):
loginButton=self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression))#"//a[@id=‘dologin‘]"
return loginButton
if __name__=="__main__":
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get("http:\mail.126.com")
lp=LoginPage(driver)
driver.switch_to.frame(lp.getFrame("xpath","//iframe[@id=‘x-URS-iframe‘]"))
time.sleep(2)
lp.getUserName("xpath","//input[@placeholder=‘邮箱帐号或手机号‘ and @name=‘email‘]").clear()
lp.getUserName("xpath","//input[@placeholder=‘邮箱帐号或手机号‘ and @name=‘email‘]").send_keys("xiaxiaoxu1987")
lp.getPassword("xpath","//input[@placeholder=‘密码‘]").send_keys("gloryroad")
lp.getLoginButton("xpath","//a[@id=‘dologin‘]").click()
driver.switch_to.default_content()
time.sleep(5)
assert u"退出" in driver.page_source, "no exist in page_source"
结果:登录成功
至此,虽然把数据作为参数传给各个找元素的函数了,但是有一个问题。
调用的时候需要手动的去输入查找元素需要的数据,这样就比较麻烦,一个字母输错了都会找不到元素,怎么弄呢?
需要再封装,把数据放在文件里,然后再封装函数从文件里读数据,这样就需要建立一个配置文件来存数据,然后新建一个python文件来封装读取配置文件的方法
步骤5—把数据放到配置文件里,然后封装方法从文件里读数据
在工程下新建一个叫Conf的包(用来放配置相关的文件):
在Conf包下新建文件:PageObjectRepository.ini
PageObjectRepository.ini:
[126mail_login]
login_page.frame=id>x-URS-iframe
login_page.username=xpath>//input[@name=‘email‘]
login_page.password=xpath>//input[@name=‘password‘]
login_page.loginbutton=id>dologin
[126mail_homepage]
home_page.addressbook=xpath>//div[text()=‘通讯录‘]
[126mail_addcontactspage]
addcontacts_page.createcontactsbtn=xpath>//span[text()=‘新建联系人‘]
addcontacts_page.contactpersonname=xpath>//a[@title=‘编辑详细姓名‘]/preceding-sibling::div/input
addcontacts_page.contactpersonemail=xpath>//*[@id=‘iaddress_MAIL_wrap‘]//input
addcontacts_page.starcontacts=xpath>//span[text()=‘设为星标联系人‘]/preceding-sibling::span/b
addcontacts_page.contactpersonmobile=xpath>//*[@id=‘iaddress_TEL_wrap‘]//dd//input
addcontacts_page.contactpersoncomment=xpath>//textarea
addcontacts_page.savecontaceperson=xpath>//span[.=‘确 定‘]
在工程下新建一个Util的包,这个包一般用来存放工具类,如读配置文件,查找元素方法,excel操作类,时间类,日志方法和操作,截图,报告模板之类的。
在Util包下新建一个python文件:ParsePageObjectRepository.py 用来封装读取配置文件的类
#encoding=utf-8
#author-夏晓旭
from ConfigParser import ConfigParser
class ParsePageObjectRepositoryConfig(object):
def __init__(self,config_path):
self.cf=ConfigParser()#生成解析器
self.cf.read(config_path)
def getItemSection(self,sectionName):
print self.cf.items(sectionName)
return dict(self.cf.items(sectionName))
def getOptionValue(self,sectionName,optionName):#返回一个字典
print self.cf.get(sectionName,optionName)
return self.cf.get(sectionName,optionName)
if __name__==‘__main__‘:
pp=ParsePageObjectRepositoryConfig("D:\test\dataDrivenTestPractice1\Conf\PageObjectRepository.ini")
print pp.getItemSection("126mail_login")
print pp.getOptionValue("126mail_login","login_page.username")
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/Util/ParsePageObjectRepository.py
[(‘login_page.frame‘, ‘id>x-URS-iframe‘), (‘login_page.username‘, "xpath>//input[@name=‘email‘]"), (‘login_page.password‘, "xpath>//input[@name=‘password‘]"), (‘login_page.loginbutton‘, ‘id>dologin‘)]
{‘login_page.loginbutton‘: ‘id>dologin‘, ‘login_page.username‘: "xpath>//input[@name=‘email‘]", ‘login_page.frame‘: ‘id>x-URS-iframe‘, ‘login_page.password‘: "xpath>//input[@name=‘password‘]"}
xpath>//input[@name=‘email‘]
xpath>//input[@name=‘email‘]
Process finished with exit code 0
可以看到测试代码中调用读取函数的时候,参数中要手动输入配置文件的路径,好长的字符,如果这个路径修改了的话,还要到这里修改,既然都封装到这里了,索性再勤快点把路径也配置在一个变量里算了
一般存文件路径的变量要放在ProjectVar包下的var.py文件里,之后所有的配置文件的路径都以变量的形式维护在这个文件中,路径的获取用相对路径的方式,稳妥一些
步骤6—用变量来存配置文件的路径
在工程下新建一个ProjectVar的包
在ProjectVar包下新建一个var.py的python文件
var.py:
#encoding=utf-8
#author-夏晓旭
import os
#获取工程所在目录的绝对路径
project_path=os.path.dirname(os.path.dirname(__file__))
#获取页面对象库文件的绝对路径
page_object_repository_path=project_path.decode("utf-8")+u"/conf/PageObjectRepository.ini"
if __name__==‘__main__‘:
print "project_path:", project_path
print "page_object_repository_path:",page_object_repository_path
print os.path.exists(project_path)
print os.path.exists(page_object_repository_path)
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/ProjectVar/var.py
project_path: D:/test/dataDrivenTestPractice1
page_object_repository_path: D:/test/dataDrivenTestPractice1/conf/PageObjectRepository.ini
True
True
Process finished with exit code 0
现在再测试一下ParsePageObjectRepository.py中读配置文件的执行
ParsePageObjectRepository.py:
#encoding=utf-8
#author-夏晓旭
from ConfigParser import ConfigParser
from ProjectVar.var import page_object_repository_path#新加的
class ParsePageObjectRepositoryConfig(object):
def __init__(self):#去掉了config_path参数
self.cf=ConfigParser()#生成解析器
self.cf.read(page_object_repository_path)#直接用变量代替
def getItemSection(self,sectionName):
print self.cf.items(sectionName)
return dict(self.cf.items(sectionName))
def getOptionValue(self,sectionName,optionName):#返回一个字典
print self.cf.get(sectionName,optionName)
return self.cf.get(sectionName,optionName)
if __name__==‘__main__‘:
pp=ParsePageObjectRepositoryConfig()#在构造函数中已经把配置文件的地址变量初始化了
print pp.getItemSection("126mail_login")
print pp.getOptionValue("126mail_login","login_page.username")
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/Util/ParsePageObjectRepository.py
[(‘login_page.frame‘, ‘id>x-URS-iframe‘), (‘login_page.username‘, "xpath>//input[@name=‘email‘]"), (‘login_page.password‘, "xpath>//input[@name=‘password‘]"), (‘login_page.loginbutton‘, ‘id>dologin‘)]
{‘login_page.loginbutton‘: ‘id>dologin‘, ‘login_page.username‘: "xpath>//input[@name=‘email‘]", ‘login_page.frame‘: ‘id>x-URS-iframe‘, ‘login_page.password‘: "xpath>//input[@name=‘password‘]"}
xpath>//input[@name=‘email‘]
xpath>//input[@name=‘email‘]
接下来修来login.py文件中的调用,把需要手动输入的参数都用读配文件代替
修改login.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from Util.ParsePageObjectRepository import *#新加
from ProjectVar.var import *#新加
class LoginPage(object):
def __init__(self,driver):
self.driver=driver
self.parse_config_file=ParsePageObjectRepositoryConfig()#新加,获取配置文件信息
self.login_page_items=self.parse_config_file.getItemSection("126mail_login")#新加
print "self.login_page_items:",self.login_page_items
self.wait = WebDriverWait(self.driver, 10, 0.2) # 显示等待
def getFrame(self):#参数去掉,在里面处理
locateType,locateExpression=self.login_page_items[‘login_page.frame‘].split(‘>‘)#id>x-URS-iframe
frame=self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression))#"//iframe[@id=‘x-URS-iframe‘]"
return frame
def getUserName(self): #参数去掉,在里面处理
locateType, locateExpression = self.login_page_items[‘login_page.username‘].split(‘>‘)#xpath>//input[@name=‘email‘]
userName=self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression))#"//input[@placeholder=‘邮箱帐号或手机号‘ and @name=‘email‘]"
return userName
def getPassword(self): #参数去掉,在里面处理
locateType, locateExpression = self.login_page_items[‘login_page.password‘].split(‘>‘)#xpath>//input[@name=‘password‘]
password=self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression))#"//input[@placeholder=‘密码‘]"
return password
def getLoginButton(self): #参数去掉,在里面处理
locateType, locateExpression = self.login_page_items[‘login_page.loginbutton‘].split(‘>‘) #id>dologin
loginButton=self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression))#"//a[@id=‘dologin‘]"
return loginButton
if __name__=="__main__":
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get("http:\mail.126.com")
lp=LoginPage(driver)
driver.switch_to.frame(lp.getFrame())
time.sleep(2)
lp.getUserName().clear()
lp.getUserName().send_keys("xiaxiaoxu1987")
lp.getPassword().send_keys("gloryroad")
lp.getLoginButton().click()
driver.switch_to.default_content()
time.sleep(5)
assert u"退出" in driver.page_source, "no exist in page_source"
至此,我们已经封装了很多东西,也把登录的数据放在配置文件里,实现读取,但是距离完美还有很大的距离,但是没有关系,只要我们清楚思路,完美只是时间问题,现在来看下这个框架还有什么问题需要优化
可以看到以下几点可以优化:
1-在login_page.py文件中的获取登录页面元素的方法中,下面这个找元素的步骤是可以封装起来的,
self.wait.until(lambda x: x.find_element(by=locateType,value=locateExpression)),就不用在每个找元素的方法中写这个了,而且这个找元素的步骤在给账户添加联系人的功能中也会用到,把找元素的方法封装起来就方便多了。
2-其次是在执行登录的操作中,用户名和密码信息是我们手动输入的,这个也是可以封装起来的,如何封装呢,起始login.py这个文件中的每个方法得到的都是登录时所需要的元素,在进行登录操作的时候我们手动的调用这几个方法,获取元素后,进行输入字符或者点击的动作来完成登录操作的,那这几个步骤就可以在另外一个文件中进行封装,说白了就是把login_page中的获取各个元素的方法进行一个封装来实现登录的功能,不用每次自己来组织零散的方法了,把他放到一个盒子里,盒子的名字叫login,每次执行这个盒子就行了,不用管里面的细节,所有的封装都是这个道理。
步骤7—封装查找元素的功能和login功能的进一步封装
在开始的框架结构中可以看到,查找元素的方法可以放到Util包中
在Util包中新建ObjectMap.py文件
ObjectMap.py:
#encoding=utf-8
#author-夏晓旭
from selenium.webdriver.support.ui import WebDriverWait
#获取单个元素对象
def getElement(driver,locateType,locateExpression):
try:
element=WebDriverWait(driver,10).until(lambda x:x.find_element(by=locateType,value=locateExpression))
return element
except Exception,e:
raise e
#获取多个相同页面元素对象,以list返回
def getElements(driver,locateType,locatorExpression):
try:
elements=WebDriverWait(driver,5).until(lambda x:x.find_elements(by=locateType,value=locateExpression))
return elements
except Exception,e:
raise e
if __name__=="__main__":
#测试代码
from selenium import webdriver
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://www.baidu.com‘)
searchBox=getElement(driver,"xpath","//input[@id=‘kw‘]")
driver.quit()
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/Util/ObjectMap.py
Process finished with exit code 0
至此查找元素的方法封装起来了,下面修改Login_Page.py来调用ObjectMap的方法实现各个查找元素的方法,并且对等登录动作做个封装。
Login_page.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from Util.ParsePageObjectRepository import *
from ProjectVar.var import *
from Util.ObjectMap import *
class LoginPage(object):
def __init__(self,driver):
self.driver=driver
self.parse_config_file=ParsePageObjectRepositoryConfig()
self.login_page_items=self.parse_config_file.getItemSection("126mail_login")
print "self.login_page_items:",self.login_page_items
self.wait = WebDriverWait(self.driver, 10, 0.2) # 显示等待
def getFrame(self):
locateType,locateExpression=self.login_page_items[‘login_page.frame‘].split(‘>‘)#id>x-URS-iframe
frame=getElement(self.driver,locateType,locateExpression)#"//iframe[@id=‘x-URS-iframe‘]"
return frame
def getUserName(self):
locateType, locateExpression = self.login_page_items[‘login_page.username‘].split(‘>‘)#xpath>//input[@name=‘email‘]
userName=getElement(self.driver,locateType,locateExpression)#"//input[@placeholder=‘邮箱帐号或手机号‘ and @name=‘email‘]"
return userName
def getPassword(self):
locateType, locateExpression = self.login_page_items[‘login_page.password‘].split(‘>‘)#xpath>//input[@name=‘password‘]
password=getElement(self.driver,locateType,locateExpression)#"//input[@placeholder=‘密码‘]"
return password
def getLoginButton(self):
locateType, locateExpression = self.login_page_items[‘login_page.loginbutton‘].split(‘>‘) #id>dologin
loginButton=getElement(self.driver,locateType,locateExpression)#"//a[@id=‘dologin‘]"
return loginButton
def login(self):
self.driver.switch_to.frame(self.getFrame())
self.getUserName().clear()
self.getUserName().send_keys("xiaxiaoxu1987")
self.getPassword().send_keys("gloryroad")
self.getLoginButton().click()
if __name__=="__main__":
#测试代码
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get("http:\mail.126.com")
lp=LoginPage(driver)
lp.login()
结果:可以登录
C:Python27python.exe D:/test/dataDrivenTestPractice1/PageObject/Login_Page.py
self.login_page_items: {‘login_page.loginbutton‘: ‘id>dologin‘, ‘login_page.username‘: "xpath>//input[@name=‘email‘]", ‘login_page.frame‘: ‘id>x-URS-iframe‘, ‘login_page.password‘: "xpath>//input[@name=‘password‘]"}
Process finished with exit code 0
下面我们把login函数的封装从Login_page.py中挪出去,作为单独的一个文件存在,把方法和调用分隔开,以后如果有变动,修改login_page.py就可以了。
步骤8—单独封装login方法
在工程下新建一个Action的包
在Action包下新建login.py
Login.py:
#encoding=utf-8
#author-夏晓旭
from PageObject.Login_Page import *
from selenium import webdriver
def login(driver,username,password):
lp=LoginPage(driver)
driver.switch_to.frame(lp.getFrame())
lp.getUserName().clear()
lp.getUserName().send_keys(username)
lp.getPassword().send_keys(password)
lp.getLoginButton().click()
if __name__==‘__main__‘:
#测试代码
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get("http://mail.126.com")
login(driver,"xiaxiaoxu1987","gloryroad")
结果:登录成功
C:Python27python.exe D:/test/dataDrivenTestPractice1/Action/login.py
self.login_page_items: {‘login_page.loginbutton‘: ‘id>dologin‘, ‘login_page.username‘: "xpath>//input[@name=‘email‘]", ‘login_page.frame‘: ‘id>x-URS-iframe‘, ‘login_page.password‘: "xpath>//input[@name=‘password‘]"}
Process finished with exit code 0
至此,登录功能基本封装好了,数据和程序分离了,下面来看下添加联系人的封装,一步一步的实现数据和程序的分离,之气已经把添加联系人的操作都放到AddressBook.py里面了,但是跟一开始的login_page.py情况一样,查找元素操作、读取配置文件数据还没有做,下面开始做这个
步骤9—封装添加联系人操作
思路:
在AddressBook.py里把添加联系人页面需要的各个元素封装成方法,其中找各个元素需要的xpath数据放在配置文件中,然后用一个函数来调用这些元素的方法,实现输入字符或者点击操作来实现添加联系人功能
AddressBook.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from PageObject.Login_Page import *
from Action.login import *
class AddressBook(object):
def __init__(self,driver):
self.driver=driver
self.parse_config_file=ParsePageObjectRepositoryConfig()
self.address_book_page=self.parse_config_file.getItemsFromSection("126mail_homepage")
print "self.address_book_page:",self.address_book_page
self.address_book_page_itmes=self.parse_config_file.getItemsFromSection("126mail_addcontactspage")
print "self.address_book_page_itmes:", self.address_book_page_itmes
def address_book_link(self):
locateType,locateExpression = self.address_book_page[‘home_page.addressbook‘].split(">")
print locateType,locateExpression
return getElement(self.driver,"xpath","//div[text()=‘通讯录‘]")
def add_contact_button(self):
locateType,locateExpression=self.address_book_page_itmes[‘addcontacts_page.createcontactsbtn‘].split(">")
print locateType,locateExpression
return getElement(self.driver,locateType,locateExpression)
def contact_name(self):
locateType,locateExpression=self.address_book_page_itmes[‘addcontacts_page.contactpersonname‘].split(">")
print locateType, locateExpression
return getElement(self.driver, locateType, locateExpression)
def contact_email(self):
locateType, locateExpression = self.address_book_page_itmes[‘addcontacts_page.contactpersonemail‘].split(">")
print locateType, locateExpression
return getElement(self.driver, locateType, locateExpression)
def contact_is_star(self):
locateType, locateExpression = self.address_book_page_itmes[‘addcontacts_page.starcontacts‘].split(">")
print locateType, locateExpression
return getElement(self.driver, locateType, locateExpression)
def contact_mobile(self):
locateType, locateExpression = self.address_book_page_itmes[‘addcontacts_page.contactpersonmobile‘].split(">")
print locateType, locateExpression
return getElement(self.driver, locateType, locateExpression)
def contact_other_info(self):
locateType, locateExpression = self.address_book_page_itmes[‘addcontacts_page.contactpersoncomment‘].split(">")
print locateType, locateExpression
return getElement(self.driver, locateType, locateExpression)
def contact_save_button(self):
locateType, locateExpression = self.address_book_page_itmes[‘addcontacts_page.savecontaceperson‘].split(">")
print locateType, locateExpression
return getElement(self.driver, locateType, locateExpression)
if __name__=="__main__":
driver = webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get("http://mail.126.com")
login(driver, "xiaxiaoxu1987", "gloryroad")
addressbook=AddressBook(driver)
time.sleep(5)
addressbook.address_book_link().click()
addressbook.add_contact_button().click()
addressbook.contact_name().send_keys("sam")
addressbook.contact_email().send_keys("[email protected]")
addressbook.contact_is_star().click()
addressbook.contact_mobile().send_keys("18142244444")
addressbook.contact_other_info().send_keys("myself")
addressbook.contact_save_button().click()
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/PageObject/AddressBook.py
self.login_page_items: {‘login_page.loginbutton‘: ‘id>dologin‘, ‘login_page.username‘: "xpath>//input[@name=‘email‘]", ‘login_page.frame‘: ‘id>x-URS-iframe‘, ‘login_page.password‘: "xpath>//input[@name=‘password‘]"}
self.address_book_page: {‘home_page.addressbook‘: "xpath>//div[text()=‘xe9x80x9axe8xaexafxe5xbdx95‘]"}
self.address_book_page_itmes: {‘addcontacts_page.createcontactsbtn‘: "xpath>//span[text()=‘xe6x96xb0xe5xbbxbaxe8x81x94xe7xb3xbbxe4xbaxba‘]", ‘addcontacts_page.contactpersonmobile‘: "xpath>//*[@id=‘iaddress_TEL_wrap‘]//dd//input", ‘addcontacts_page.contactpersonemail‘: "xpath>//*[@id=‘iaddress_MAIL_wrap‘]//input", ‘addcontacts_page.contactpersoncomment‘: ‘xpath>//textarea‘, ‘addcontacts_page.contactpersonname‘: "xpath>//a[@title=‘xe7xbcx96xe8xbex91xe8xafxa6xe7xbbx86xe5xa7x93xe5x90x8d‘]/preceding-sibling::div/input", ‘addcontacts_page.savecontaceperson‘: "xpath>//span[.=‘xe7xa1xae xe5xaex9a‘]", ‘addcontacts_page.starcontacts‘: "xpath>//span[text()=‘xe8xaexbexe4xb8xbaxe6x98x9fxe6xa0x87xe8x81x94xe7xb3xbbxe4xbaxba‘]/preceding-sibling::span/b"}
xpath //div[text()=‘通讯录‘]
xpath //span[text()=‘新建联系人‘]
xpath //a[@title=‘编辑详细姓名‘]/preceding-sibling::div/input
xpath //*[@id=‘iaddress_MAIL_wrap‘]//input
xpath //span[text()=‘设为星标联系人‘]/preceding-sibling::span/b
xpath //*[@id=‘iaddress_TEL_wrap‘]//dd//input
xpath //textarea
xpath //span[.=‘确 定‘]
现在,我们已经把添加联系人页面的各个元素操作封装起来了,在测试代码中进行了调用,同login功能的封装一样,我们把对添加联系人页面的各个元素的操作封装在另一个文件中,在主程序中直接调用就可以了,方便操作。
步骤10—单独封装addressBook的添加联系人功能
在Action包下新建add_contact.py文件
add_contact.py:
#encoding=utf-8
#author-夏晓旭
from PageObject.AddressBook import *
from selenium import webdriver
def add_contact(driver):
driver.switch_to.default_content()
addressbook = AddressBook(driver)
addressbook.address_book_link().click()
addressbook.add_contact_button().click()
addressbook.contact_name().send_keys("sam")
addressbook.contact_email().send_keys("[email protected]")
addressbook.contact_is_star().click()
addressbook.contact_mobile().send_keys("18142244444")
addressbook.contact_other_info().send_keys("myself")
addressbook.contact_save_button().click()
if __name__==‘__main__‘:
driver=webdriver.Firefox(executable_path="c:\geckodriver")
driver.get("http://mail.126.com")
login(driver, "xiaxiaoxu1987", "gloryroad")
add_contact(driver)
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/Action/add_contact.py
self.login_page_items: {‘login_page.loginbutton‘: ‘id>dologin‘, ‘login_page.username‘: "xpath>//input[@name=‘email‘]", ‘login_page.frame‘: ‘id>x-URS-iframe‘, ‘login_page.password‘: "xpath>//input[@name=‘password‘]"}
Process finished with exit code 0
进一步封装,把输入的部分用参数代替
#encoding=utf-8
#author-夏晓旭
from PageObject.AddressBook import *
from selenium import webdriver
def add_contact(driver,name="",email="",is_star=True,mobile="",otherinfo=""):
driver.switch_to.default_content()
addressbook = AddressBook(driver)
addressbook.address_book_link().click()
addressbook.add_contact_button().click()
addressbook.contact_name().send_keys(name)
addressbook.contact_email().send_keys(email)
if is_star==True:
addressbook.contact_is_star().click()
addressbook.contact_mobile().send_keys(mobile)
addressbook.contact_other_info().send_keys(otherinfo)
addressbook.contact_save_button().click()
if __name__==‘__main__‘:
driver=webdriver.Firefox(executable_path="c:\geckodriver")
driver.get("http://mail.126.com")
login(driver, "xiaxiaoxu1987", "gloryroad")
add_contact(driver,"xiaxiaoxu","[email protected]",True,"13333334444","gloryroad")
driver.quit()
结果:ok
C:Python27python.exe D:/test/dataDrivenTestPractice1/Action/add_contact.py
self.login_page_items: {‘login_page.loginbutton‘: ‘id>dologin‘, ‘login_page.username‘: "xpath>//input[@name=‘email‘]", ‘login_page.frame‘: ‘id>x-URS-iframe‘, ‘login_page.password‘: "xpath>//input[@name=‘password‘]"}
Process finished with exit code 0
至此,我们已经封装了大部分功能,现在来试一下主程序中调用这几个功能:
TestScript.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from PageObject.Login_Page import *
from PageObject.AddressBook import *
from Action.add_contact import *
from Action.login import *
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://mail.126.com‘)
login(driver, "xiaxiaoxu1987", "gloryroad")
add_contact(driver,"xiaxiaoxu","[email protected]",True,"13333334444","gloryroad")
driver.quit()
结果:可以正常运行。
C:Python27python.exe D:/test/dataDrivenTestPractice1/TestScript/TestScript.py
Process finished with exit code 0
从主程序中的调用来看,还是有一些数据是需要手动来输入的,如用户名、密码、需要添加的联系人信息,如果需要添加多个联系人的话,还需要多次输入,下一步就来看怎么把这部分数据和程序分离开,也是存到文件中,这次来放Excel中,然后用操作excel的方法来出取数据。
步骤11-封装excel操作
创建一个excel文件,第一个sheet来放126账号的信息
第二个sheet放联系人:
在工程下新建一个TestData的包,把excel文件放到该包下
Excel.py:
#encoding=utf-8
#author-夏晓旭
# encoding=utf-8
from openpyxl import load_workbook
from openpyxl.styles import Border, Side, Font
import time
from ProjectVar.var import *
class parseExcel(object):
def __init__(self, excelPath):
self.excelPath = excelPath
self.workbook = load_workbook(excelPath) # 加载excel
self.sheet = self.workbook.active # 获取第一个sheet
self.font = Font(color=None)
self.colorDict = {"red": ‘FFFF3030‘, "green": ‘FF008B00‘}
# 设置当前要操作的sheet对象,使用index来获取相应的sheet
def set_sheet_by_index(self, sheet_index):
sheet_name = self.workbook.get_sheet_names()[sheet_index]
self.sheet = self.workbook.get_sheet_by_name(sheet_name)
return self.sheet
# 获取当前默认sheet的名字
def get_default_sheet(self):
return self.sheet.title
# 设置当前要操作的sheet对象,使用sheet名称来获取相应的sheet
def set_sheet_by_name(self, sheet_name):
sheet = self.workbook.get_sheet_by_name(sheet_name)
self.sheet = sheet
return self.sheet
# 获取默认sheet中最大的行数
def get_max_row_no(self):
return self.sheet.max_row
# 获取默认 sheet 的最大列数
def get_max_col_no(self):
return self.sheet.max_column
# 获取默认sheet的最小(起始)行号
def get_min_row_no(self):
return self.sheet.min_row
# 获取默认sheet的最小(起始)列号
def get_min_col_no(self):
return self.sheet.min_column
# 获取默认 sheet 的所有行对象,
def get_all_rows(self):
return list(self.sheet.iter_rows())
# return list(self.rows)也可以
# 获取默认sheet中的所有列对象
def get_all_cols(self):
return list(self.sheet.iter_cols())
# return list(self.sheet.columns)也可以
# 从默认sheet中获取某一列,第一列从0开始
def get_single_col(self, col_no):
return self.get_all_cols()[col_no]
# 从默认sheet中获取某一行,第一行从0开始
def get_single_row(self, row_no):
return self.get_all_rows()[row_no]
# 从默认sheet中,通过行号和列号获取指定的单元格,注意行号和列号从1开始
def get_cell(self, row_no, col_no):
return self.sheet.cell(row=row_no, column=col_no)
# 从默认sheet中,通过行号和列号获取指定的单元格中的内容,注意行号和列号从1开始
def get_cell_content(self, row_no, col_no):
return self.sheet.cell(row=row_no, column=col_no).value
# 从默认sheet中,通过行号和列号向指定单元格中写入指定内容,注意行号和列号从1开始
# 调用此方法的时候,excel不要处于打开状态
def write_cell_content(self, row_no, col_no, content, font=None):
self.sheet.cell(row=row_no, column=col_no).value = content
self.workbook.save(self.excelPath)
return self.sheet.cell(row=row_no, column=col_no).value
# 从默认sheet中,通过行号和列号向指定单元格中写入当前日期,注意行号和列号从1开始
# 调用此方法的时候,excel不要处于打开状态
def write_cell_current_time(self, row_no, col_no):
time1 = time.strftime("%Y-%m-%d %H:%M:%S")
self.sheet.cell(row=row_no, column=col_no).value = str(time1)
self.workbook.save(self.excelPath)
return self.sheet.cell(row=row_no, column=col_no).value
def save_excel_file(self):
self.workbook.save(self.excelPath)
if __name__ == ‘__main__‘:
#测试代码
p = parseExcel(test_data_excel_path)
print u"获取默认sheet:", p.get_default_sheet()
print u"设置sheet索引为1", p.set_sheet_by_index(1)
print u"获取默认sheet:", p.get_default_sheet()
print u"设置sheet索引为0", p.set_sheet_by_index(0)
print u"获取默认sheet:", p.get_default_sheet()
print u"最大行数:", p.get_max_row_no()
print u"最大列数:", p.get_max_col_no()
print u"最小起始行数:", p.get_min_row_no()
print u"最小起始列数:", p.get_min_col_no()
print u"所有行对象:", p.get_all_rows()
print u"所有列对象:", p.get_all_cols()
print u"获取某一列(2):", p.get_single_col(2)
print u"获取某一行(1):", p.get_single_row(1)
print u"取得行号和列号(2,2)单元格:", p.get_cell(2, 2)
print u"取得行号和列号单元格的内容(2,2)", p.get_cell_content(2, 2)
print u"行号和列号写入内容(11,11):‘xiaxiaoxu‘", p.write_cell_content(11, 11, ‘xiaxiaoxu‘) #
print u"行号和列号写入当前日期(13,13):", p.write_cell_current_time(13, 13)
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/Util/Excel.py
获取默认sheet: 联系人
设置sheet索引为1 <Worksheet "u8054u7cfbu4eba">
获取默认sheet: 联系人
设置sheet索引为0 <Worksheet "126u8d26u53f7">
获取默认sheet: 126账号
最大行数: 3
最大列数: 6
最小起始行数: 1
最小起始列数: 1
所有行对象: [(<Cell u‘126u8d26u53f7‘.A1>, <Cell u‘126u8d26u53f7‘.B1>, <Cell u‘126u8d26u53f7‘.C1>, <Cell u‘126u8d26u53f7‘.D1>, <Cell u‘126u8d26u53f7‘.E1>, <Cell u‘126u8d26u53f7‘.F1>), (<Cell u‘126u8d26u53f7‘.A2>, <Cell u‘126u8d26u53f7‘.B2>, <Cell u‘126u8d26u53f7‘.C2>, <Cell u‘126u8d26u53f7‘.D2>, <Cell u‘126u8d26u53f7‘.E2>, <Cell u‘126u8d26u53f7‘.F2>), (<Cell u‘126u8d26u53f7‘.A3>, <Cell u‘126u8d26u53f7‘.B3>, <Cell u‘126u8d26u53f7‘.C3>, <Cell u‘126u8d26u53f7‘.D3>, <Cell u‘126u8d26u53f7‘.E3>, <Cell u‘126u8d26u53f7‘.F3>)]
所有列对象: [(<Cell u‘126u8d26u53f7‘.A1>, <Cell u‘126u8d26u53f7‘.A2>, <Cell u‘126u8d26u53f7‘.A3>), (<Cell u‘126u8d26u53f7‘.B1>, <Cell u‘126u8d26u53f7‘.B2>, <Cell u‘126u8d26u53f7‘.B3>), (<Cell u‘126u8d26u53f7‘.C1>, <Cell u‘126u8d26u53f7‘.C2>, <Cell u‘126u8d26u53f7‘.C3>), (<Cell u‘126u8d26u53f7‘.D1>, <Cell u‘126u8d26u53f7‘.D2>, <Cell u‘126u8d26u53f7‘.D3>), (<Cell u‘126u8d26u53f7‘.E1>, <Cell u‘126u8d26u53f7‘.E2>, <Cell u‘126u8d26u53f7‘.E3>), (<Cell u‘126u8d26u53f7‘.F1>, <Cell u‘126u8d26u53f7‘.F2>, <Cell u‘126u8d26u53f7‘.F3>)]
获取某一列(2): (<Cell u‘126u8d26u53f7‘.C1>, <Cell u‘126u8d26u53f7‘.C2>, <Cell u‘126u8d26u53f7‘.C3>)
获取某一行(1): (<Cell u‘126u8d26u53f7‘.A2>, <Cell u‘126u8d26u53f7‘.B2>, <Cell u‘126u8d26u53f7‘.C2>, <Cell u‘126u8d26u53f7‘.D2>, <Cell u‘126u8d26u53f7‘.E2>, <Cell u‘126u8d26u53f7‘.F2>)
取得行号和列号(2,2)单元格: <Cell u‘126u8d26u53f7‘.B2>
取得行号和列号单元格的内容(2,2) xiaxiaoxu1987
行号和列号写入内容(11,11):‘xiaxiaoxu‘ xiaxiaoxu
行号和列号写入当前日期(13,13): 2018-07-10 21:28:14
Process finished with exit code 0
至此,对excel操作就封装完了,下面在主程序中试一下对数据的读取
TestScript.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from PageObject.Login_Page import *
from PageObject.AddressBook import *
from Action.add_contact import *
from Action.login import *
from ProjectVar.var import *
from Util.Excel import *
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://mail.126.com‘)
pe=parseExcel(test_data_excel_path)
pe.set_sheet_by_name(u"126账号")
print pe.get_default_sheet()
rows=pe.get_all_rows()[1:]
for id,row in enumerate(rows):
if row[4].value ==‘y‘:
username = row[1].value
password = row[2].value
print username, password
try:
login(driver,username,password)
pe.set_sheet_by_name(u"联系人")
print pe.get_default_sheet()
rows1=pe.get_all_rows()[1:]
print "rows1:",rows1
for id1,row in enumerate(rows1):
if row[7].value == ‘y‘:
try:
#print row[1].value,row[2].value,row[3].value,row[4].value,row[5].value
#print "execute1"
add_contact(driver,row[1].value,row[2].value,row[3].value,row[4].value,row[5].value)
print "assert word:",row[6].value in driver.page_source
print row[6].value
pe.write_cell_content(id1+2,10,"pass")
except Exception,e:
print u"异常信息:",e.message
pe.write_cell_content(id1+2,10,"fail")
else:
pe.write_cell_content(id1+2,10,u"忽略")
continue
except Exception,e:
pe.set_sheet_by_name(u"126账号")
print u"异常信息:",e
pe.write_cell_content(id+2,5,"fail")
else:
pe.set_sheet_by_name(u"126账号")
pe.write_cell_content(id+2,6,u"忽略")
continue
结果:执行ok
C:Python27python.exe D:/test/dataDrivenTestPractice1/TestScript/TestScript.py
126账号
xiaxiaoxu1987 gloryroad
联系人
rows1: [(<Cell u‘u8054u7cfbu4eba‘.A2>, <Cell u‘u8054u7cfbu4eba‘.B2>, <Cell u‘u8054u7cfbu4eba‘.C2>, <Cell u‘u8054u7cfbu4eba‘.D2>, <Cell u‘u8054u7cfbu4eba‘.E2>, <Cell u‘u8054u7cfbu4eba‘.F2>, <Cell u‘u8054u7cfbu4eba‘.G2>, <Cell u‘u8054u7cfbu4eba‘.H2>, <Cell u‘u8054u7cfbu4eba‘.I2>, <Cell u‘u8054u7cfbu4eba‘.J2>), (<Cell u‘u8054u7cfbu4eba‘.A3>, <Cell u‘u8054u7cfbu4eba‘.B3>, <Cell u‘u8054u7cfbu4eba‘.C3>, <Cell u‘u8054u7cfbu4eba‘.D3>, <Cell u‘u8054u7cfbu4eba‘.E3>, <Cell u‘u8054u7cfbu4eba‘.F3>, <Cell u‘u8054u7cfbu4eba‘.G3>, <Cell u‘u8054u7cfbu4eba‘.H3>, <Cell u‘u8054u7cfbu4eba‘.I3>, <Cell u‘u8054u7cfbu4eba‘.J3>), (<Cell u‘u8054u7cfbu4eba‘.A4>, <Cell u‘u8054u7cfbu4eba‘.B4>, <Cell u‘u8054u7cfbu4eba‘.C4>, <Cell u‘u8054u7cfbu4eba‘.D4>, <Cell u‘u8054u7cfbu4eba‘.E4>, <Cell u‘u8054u7cfbu4eba‘.F4>, <Cell u‘u8054u7cfbu4eba‘.G4>, <Cell u‘u8054u7cfbu4eba‘.H4>, <Cell u‘u8054u7cfbu4eba‘.I4>, <Cell u‘u8054u7cfbu4eba‘.J4>), (<Cell u‘u8054u7cfbu4eba‘.A5>, <Cell u‘u8054u7cfbu4eba‘.B5>, <Cell u‘u8054u7cfbu4eba‘.C5>, <Cell u‘u8054u7cfbu4eba‘.D5>, <Cell u‘u8054u7cfbu4eba‘.E5>, <Cell u‘u8054u7cfbu4eba‘.F5>, <Cell u‘u8054u7cfbu4eba‘.G5>, <Cell u‘u8054u7cfbu4eba‘.H5>, <Cell u‘u8054u7cfbu4eba‘.I5>, <Cell u‘u8054u7cfbu4eba‘.J5>)]
assert word: True
assert word: True
李四
Process finished with exit code 0
Excel结果:
至此,该框架大部分功能已经封装好了,下面再搞一下日志模块,使程序在执行的时候能够打印日志
步骤12—添加日志log模块
在Conf包下新建Logger.conf日志的配置文件,其中配置了日志器、处理器和格式器,日志这一块具体的只是后续会专门列一个整理
#logger.conf
###############################################
[loggers]
keys=root,example01,example02
[logger_root]
level=DEBUG
handlers=hand01,hand02
[logger_example01]
handlers=hand01,hand02
qualname=example01
propagate=0
[logger_example02]
handlers=hand01,hand03
qualname=example02
propagate=0
###############################################
[handlers]
keys=hand01,hand02,hand03
[handler_hand01]
class=StreamHandler
level=INFO
formatter=form01
args=(sys.stderr,)
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form01
args=(‘DataDrivenFrameWork.log‘, ‘a‘)
[handler_hand03]
class=handlers.RotatingFileHandler
level=INFO
formatter=form01
args=(‘DataDrivenFrameWork.log‘, ‘a‘, 10*1024*1024, 5)
###############################################
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%Y-%m-%d %H:%M:%S
[formatter_form02]
format=%(name)-12s: %(levelname)-8s %(message)s
datefmt=%Y-%m-%d %H:%M:%S
在Util包下新建log.py文件,来封装日志的操作
Log.py:
#encoding=utf-8
#author-夏晓旭
import logging
import logging.config
from ProjectVar.var import *
#读取日志的配置文件
logging.config.fileConfig(project_path+"\Conf\Logger.conf")
#选择一个日志格式
logger=logging.getLogger("example02")
def error(message):
#打印debug级别的信息
logger.error(message)
def info(message):
#打印info级别的信息
logger.info(message)
def warning(message):
#打印warning级别的信息
logger.warning(message)
if __name__=="__main__":
#测试代码
info("hi")
print "config file path:",project_path+"\Conf\Logger.conf"
error("world!")
warning("gloryroad!")
结果:
C:Python27python.exe D:/test/dataDrivenTestPractice1/Util/log.py
2018-07-11 21:32:12 log.py[line:19] INFO hi
config file path: D: estdataDrivenTestPractice1ConfLogger.conf
2018-07-11 21:32:12 log.py[line:15] ERROR world!
2018-07-11 21:32:12 log.py[line:23] WARNING gloryroad!
Process finished with exit code 0
日志的封装已经ok了,下面修改主程序并且调用日志。
TestScript.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from PageObject.Login_Page import *
from PageObject.AddressBook import *
from Action.add_contact import *
from Action.login import *
from ProjectVar.var import *
from Util.Excel import *
from Util.log import *
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://mail.126.com‘)
pe=parseExcel(test_data_excel_path)
pe.set_sheet_by_name(u"126账号")
print pe.get_default_sheet()
rows=pe.get_all_rows()[1:]
for id,row in enumerate(rows):
if row[4].value ==‘y‘:
username = row[1].value
password = row[2].value
print username, password
try:
login(driver,username,password)
pe.set_sheet_by_name(u"联系人")
print pe.get_default_sheet()
rows1=pe.get_all_rows()[1:]
print "rows1:",rows1
test_data_pass_flag=True#结果表示,用于最后写入excel结果
for id1,row in enumerate(rows1):
if row[7].value == ‘y‘:
try:
#print row[1].value,row[2].value,row[3].value,row[4].value,row[5].value
print "log0711",row[1],row[1].value
print "log0711",type(row[1].value)
#进行添加联系人的实际操作
add_contact(driver,row[1].value,row[2].value,row[3].value,row[4].value,row[5].value)
print "assert word:",row[6].value
assert row[6].value in driver.page_source
pe.write_cell_content(id1+2,10,"pass")#assert没有报错,说明断言成功
except Exception,e:
#print u"异常信息01",e.message
pe.write_cell_content(id1+2,10,"fail")
test_data_pass_flag=False#代码走到这里,说明有异常,测试失败
info(u"异常信息01" + e.message) # 输出日志
else:#说明标识为‘n‘,忽略该数据
pe.write_cell_content(id1+2,10,u"忽略")
continue
if test_data_pass_flag ==True:#如果标识是True,说明结果是成功的
pe.set_sheet_by_name(u"126账号")
pe.write_cell_content(id+2,5,"成功")
else:#说明标识是False
pe.set_sheet_by_name(u"126账号")
pe.write_cell_content(id+2,5,"失败")
except Exception,e:
pe.set_sheet_by_name(u"126账号")
#print u"异常信息02:",e
pe.write_cell_content(id+2,6,"fail")
info(u"异常信息02:"+e.message)#输出日志
time.sleep(2)
driver.quit()
else:#走这个分支,说明标识为"n"
pe.set_sheet_by_name(u"126账号")
pe.write_cell_content(id+2,6,u"忽略")
continue
结果:运行ok
C:Python27python.exe D:/test/dataDrivenTestPractice1/TestScript/TestScript.py
126账号
xiaxiaoxu1987 gloryroad
2018-07-11 22:55:25 log.py[line:19] INFO login successfully
联系人
rows1: [(<Cell u‘u8054u7cfbu4eba‘.A2>, <Cell u‘u8054u7cfbu4eba‘.B2>, <Cell u‘u8054u7cfbu4eba‘.C2>, <Cell u‘u8054u7cfbu4eba‘.D2>, <Cell u‘u8054u7cfbu4eba‘.E2>, <Cell u‘u8054u7cfbu4eba‘.F2>, <Cell u‘u8054u7cfbu4eba‘.G2>, <Cell u‘u8054u7cfbu4eba‘.H2>, <Cell u‘u8054u7cfbu4eba‘.I2>, <Cell u‘u8054u7cfbu4eba‘.J2>), (<Cell u‘u8054u7cfbu4eba‘.A3>, <Cell u‘u8054u7cfbu4eba‘.B3>, <Cell u‘u8054u7cfbu4eba‘.C3>, <Cell u‘u8054u7cfbu4eba‘.D3>, <Cell u‘u8054u7cfbu4eba‘.E3>, <Cell u‘u8054u7cfbu4eba‘.F3>, <Cell u‘u8054u7cfbu4eba‘.G3>, <Cell u‘u8054u7cfbu4eba‘.H3>, <Cell u‘u8054u7cfbu4eba‘.I3>, <Cell u‘u8054u7cfbu4eba‘.J3>), (<Cell u‘u8054u7cfbu4eba‘.A4>, <Cell u‘u8054u7cfbu4eba‘.B4>, <Cell u‘u8054u7cfbu4eba‘.C4>, <Cell u‘u8054u7cfbu4eba‘.D4>, <Cell u‘u8054u7cfbu4eba‘.E4>, <Cell u‘u8054u7cfbu4eba‘.F4>, <Cell u‘u8054u7cfbu4eba‘.G4>, <Cell u‘u8054u7cfbu4eba‘.H4>, <Cell u‘u8054u7cfbu4eba‘.I4>, <Cell u‘u8054u7cfbu4eba‘.J4>), (<Cell u‘u8054u7cfbu4eba‘.A5>, <Cell u‘u8054u7cfbu4eba‘.B5>, <Cell u‘u8054u7cfbu4eba‘.C5>, <Cell u‘u8054u7cfbu4eba‘.D5>, <Cell u‘u8054u7cfbu4eba‘.E5>, <Cell u‘u8054u7cfbu4eba‘.F5>, <Cell u‘u8054u7cfbu4eba‘.G5>, <Cell u‘u8054u7cfbu4eba‘.H5>, <Cell u‘u8054u7cfbu4eba‘.I5>, <Cell u‘u8054u7cfbu4eba‘.J5>)]
log0711 <Cell u‘u8054u7cfbu4eba‘.B2> lily
log0711 <type ‘unicode‘>
assert word: [email protected]
Process finished with exit code 0
至此,主程序中实现了读取数据、登录邮箱、添加联系人、往excel里写入测试结果,基本的功能已经搞定了,下面再搞一个地方,就接近完美了,就是对时间的封装,在程序中调用时间函数写入指定的时间格式。
步骤13—常用时间操作的封装
在Util包下新建FormatTime.py
FormatTime.py
#encoding=utf-8
#author-夏晓旭
#encoding=utf-8
import time
from datetime import timedelta,date
def date_time_chinese():
print u"returns the current time string,format for YYYY年mm月dd日HH时MM分SS秒"
return time.strftime("%Y年%m月%d日 %H时%M分%S秒",time.localtime())
def date_chinese():
print u"returns the current time string, format for YYYY年mm月dd日"
return time.strftime("%Y年%m月%d日",time.localtime())
def time_chinese():
print u"returns the current time string,format for HH时 MM分 SS秒"
return time.strftime("%H时%M分%S秒",time.localtime())
def date_time():
print "returns the current time string, format for YYYY-mm-dd HH:MM:SS"
return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
def date_time_slash():
print "returns the current time string,format for YYYY/mm/dd HH:MM:SS"
return time.strftime("%Y/%m/%d %H:%M:%S",time.localtime())
def dates():
print "returns the current time string,format for YYYY-mm-dd"
return time.strftime("%Y-%m-%d",time.localtime())
def date_slash():
print "returns the current time string,format for YYYY/mm/dd"
return time.strftime("%Y/%m/%d",time.localtime())
def times():
print "returns the current time string, format for HH:MM:SS"
return time.strftime("%H:%M:%S",time.localtime())
def year():
print "returns the current time string,format for year"
return time.strftime("%Y",time.localtime())
def month():
print "returns the current time string,format for month"
return time.strftime("%m",time.localtime())
def day():
print "returns the current time string,format for day"
return time.strftime("%d",time.localtime())
def hour():
print "returns the current time string, format for Hour"
return time.strftime("%H",time.localtime())
def minute():
print "returns the current time string,format for minute"
return time.strftime("%M",time.localtime())
def seconds():
print "return the current time string,format for seconds"
return time.strftime("%S",time.localtime())
def str_to_tuple(stime):
print "returns the string variable into time tuples"
return time.strptime(stime,"%Y-%m-%d %H:%M:%S")
def add_date(day_num):
today=date.today()
print "returns the current date-%s and a time interval-%s" %(today,day_num)
times=today+timedelta(days=day_num)
return times
def sub_date(day_num):
today=date.today()
print "returns the current date-%s minus one time interval-%s" %(today,day_num)
times=today-timedelta(days=day_num)
return times
if __name__==‘__main__‘:
#测试代码
print date_time_chinese()
print date_chinese()
print time_chinese()
print date_time()
print date_time_slash()
print dates()
print date_slash()
print times()
print year()
print month()
print day()
print hour()
print minute()
print seconds()
time1=time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
print str_to_tuple(time1)
print add_date(2)
print sub_date(2)
结果:ok,pycharm里中文不需要转码,在notepad里运行需要转码
C:Python27python.exe D:/test/dataDrivenTestPractice1/Util/FormatTime.py
returns the current time string,format for YYYY年mm月dd日HH时MM分SS秒
2018年07月11日 23时01分36秒
returns the current time string, format for YYYY年mm月dd日
2018年07月11日
returns the current time string,format for HH时 MM分 SS秒
23时01分36秒
returns the current time string, format for YYYY-mm-dd HH:MM:SS
2018-07-11 23:01:36
returns the current time string,format for YYYY/mm/dd HH:MM:SS
2018/07/11 23:01:36
returns the current time string,format for YYYY-mm-dd
2018-07-11
returns the current time string,format for YYYY/mm/dd
2018/07/11
returns the current time string, format for HH:MM:SS
23:01:36
returns the current time string,format for year
2018
returns the current time string,format for month
07
returns the current time string,format for day
11
returns the current time string, format for Hour
23
returns the current time string,format for minute
01
return the current time string,format for seconds
36
returns the string variable into time tuples
time.struct_time(tm_year=2018, tm_mon=7, tm_mday=11, tm_hour=23, tm_min=1, tm_sec=36, tm_wday=2, tm_yday=192, tm_isdst=-1)
returns the current date-2018-07-11 and a time interval-2
2018-07-13
returns the current date-2018-07-11 minus one time interval-2
2018-07-09
Process finished with exit code 0
修改主程序,调用时间函数,打印时间
TestScript.py:
#encoding=utf-8
#author-夏晓旭
from selenium import webdriver
import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import traceback
from PageObject.Login_Page import *
from PageObject.AddressBook import *
from Action.add_contact import *
from Action.login import *
from ProjectVar.var import *
from Util.Excel import *
from Util.log import *
from Util.FormatTime import *
driver=webdriver.Firefox(executable_path=‘c:\geckodriver‘)
driver.get(‘http://mail.126.com‘)
pe=parseExcel(test_data_excel_path)
pe.set_sheet_by_name(u"126账号")
print pe.get_default_sheet()
rows=pe.get_all_rows()[1:]
for id,row in enumerate(rows):
if row[4].value ==‘y‘:
username = row[1].value
password = row[2].value
print username, password
try:
login(driver,username,password)
pe.set_sheet_by_name(u"联系人")
print pe.get_default_sheet()
rows1=pe.get_all_rows()[1:]
print "rows1:",rows1
test_data_pass_flag=True#结果表示,用于最后写入excel结果
for id1,row in enumerate(rows1):
if row[7].value == ‘y‘:
try:
#print row[1].value,row[2].value,row[3].value,row[4].value,row[5].value
print "log0711",row[1],row[1].value
print "log0711",type(row[1].value)
#进行添加联系人的实际操作
add_contact(driver,row[1].value,row[2].value,row[3].value,row[4].value,row[5].value)
pe.write_cell_content(id1 + 2, 9, date_time())
print "assert word:",row[6].value
assert row[6].value in driver.page_source
pe.write_cell_content(id1+2,10,"pass")#assert没有报错,说明断言成功
except Exception,e:
#print u"异常信息01",e.message
pe.write_cell_content(id1+2,10,"fail")
pe.write_cell_content(id1+2,9,date_time())
test_data_pass_flag=False#代码走到这里,说明有异常,测试失败
info(u"异常信息01" + e.message) # 输出日志
else:#说明标识为‘n‘,忽略该数据
pe.write_cell_content(id1+2,10,u"忽略")
pe.write_cell_content(id1 + 2, 9,date_time())
continue
if test_data_pass_flag ==True:#如果标识是True,说明结果是成功的
pe.set_sheet_by_name(u"126账号")
pe.write_cell_content(id+2,6,"成功")
else:#说明标识是False
pe.set_sheet_by_name(u"126账号")
pe.write_cell_content(id+2,6,"失败")
except Exception,e:
pe.set_sheet_by_name(u"126账号")
#print u"异常信息02:",e
pe.write_cell_content(id+2,6,"fail")
info(u"异常信息02:"+e.message)#输出日志
time.sleep(2)
driver.quit()
else:#走这个分支,说明标识为"n"
pe.set_sheet_by_name(u"126账号")
pe.write_cell_content(id+2,6,u"忽略")
continue
结果: ok
C:Python27python.exe D:/test/dataDrivenTestPractice1/TestScript/TestScript.py
126账号
xiaxiaoxu1987 gloryroad
2018-07-11 23:17:39 log.py[line:19] INFO login successfully
联系人
rows1: [(<Cell u‘u8054u7cfbu4eba‘.A2>, <Cell u‘u8054u7cfbu4eba‘.B2>, <Cell u‘u8054u7cfbu4eba‘.C2>, <Cell u‘u8054u7cfbu4eba‘.D2>, <Cell u‘u8054u7cfbu4eba‘.E2>, <Cell u‘u8054u7cfbu4eba‘.F2>, <Cell u‘u8054u7cfbu4eba‘.G2>, <Cell u‘u8054u7cfbu4eba‘.H2>, <Cell u‘u8054u7cfbu4eba‘.I2>, <Cell u‘u8054u7cfbu4eba‘.J2>), (<Cell u‘u8054u7cfbu4eba‘.A3>, <Cell u‘u8054u7cfbu4eba‘.B3>, <Cell u‘u8054u7cfbu4eba‘.C3>, <Cell u‘u8054u7cfbu4eba‘.D3>, <Cell u‘u8054u7cfbu4eba‘.E3>, <Cell u‘u8054u7cfbu4eba‘.F3>, <Cell u‘u8054u7cfbu4eba‘.G3>, <Cell u‘u8054u7cfbu4eba‘.H3>, <Cell u‘u8054u7cfbu4eba‘.I3>, <Cell u‘u8054u7cfbu4eba‘.J3>), (<Cell u‘u8054u7cfbu4eba‘.A4>, <Cell u‘u8054u7cfbu4eba‘.B4>, <Cell u‘u8054u7cfbu4eba‘.C4>, <Cell u‘u8054u7cfbu4eba‘.D4>, <Cell u‘u8054u7cfbu4eba‘.E4>, <Cell u‘u8054u7cfbu4eba‘.F4>, <Cell u‘u8054u7cfbu4eba‘.G4>, <Cell u‘u8054u7cfbu4eba‘.H4>, <Cell u‘u8054u7cfbu4eba‘.I4>, <Cell u‘u8054u7cfbu4eba‘.J4>), (<Cell u‘u8054u7cfbu4eba‘.A5>, <Cell u‘u8054u7cfbu4eba‘.B5>, <Cell u‘u8054u7cfbu4eba‘.C5>, <Cell u‘u8054u7cfbu4eba‘.D5>, <Cell u‘u8054u7cfbu4eba‘.E5>, <Cell u‘u8054u7cfbu4eba‘.F5>, <Cell u‘u8054u7cfbu4eba‘.G5>, <Cell u‘u8054u7cfbu4eba‘.H5>, <Cell u‘u8054u7cfbu4eba‘.I5>, <Cell u‘u8054u7cfbu4eba‘.J5>)]
log0711 <Cell u‘u8054u7cfbu4eba‘.B2> lily
log0711 <type ‘unicode‘>
returns the current time string, format for YYYY-mm-dd HH:MM:SS
assert word: [email protected]
returns the current time string, format for YYYY-mm-dd HH:MM:SS
returns the current time string, format for YYYY-mm-dd HH:MM:SS
returns the current time string, format for YYYY-mm-dd HH:MM:SS
Process finished with exit code 0
Excel写入结果:
总结:
现在我们就是先从组装零散的功能实现一个较大的功能,然后把较大的功能组织成一个更大的功能,到最后在主程序中我们看到的就是独立大块儿功能的组合,看起来很整洁,简单,可读性高,也方便维护。
这样我们把整个框架从零散的罗列代码到封装成一块块的功能,把数据和程序做一个分离,把某一个独立的功能进行封装,整合,在这个过程中,就把搭框架的步骤熟悉了,知道了封装成几大块的来龙去脉和带来的好处,之后再搭建测试框架的时候,就直接按照这几大块来搭就行了,相对会比较容易接受这个思路和逻辑还有它的必要性,从根本上理解了为什么要搭建这样的测试框架,以及这个测试框架的原理,进而会举一反三,扩展到其他类型的测试框架当中。
多动手实践~
以上是关于python webdriver 一步一步搭建数据驱动测试框架的过程和总结的主要内容,如果未能解决你的问题,请参考以下文章
AngularJs + WebApi + EF + SqlServer 一步一步搭建项目