自动化测试 - 黑马头条测试项目

黑马头条测试项目

1. 自动化测试流程

1. 自动化测试流程

  1. 需求分析
  2. 挑选适合做自动化测试的功能
  3. 设计测试用例
  4. 搭建自动化测试环境
    • web自动化测试环境(4个)
      • python开发者工具(pycharm, python解释器)
      • 浏览器
      • 浏览器驱动
      • selenium
    • app自动化测试环境(5个)
      • 手机或模拟器
      • 安卓sdk
      • python开发工具
      • appium服务器
      • appium服务客户端
  5. 设计自动化测试项目架构
  6. 编写代码
  7. 执行测试用例
  8. 生成测试报告并分析结果

2. 黑马头条项目简介

1. 项目背景

作为一个IT教育机构,拥有自己开发且实际运营的产品,将开发和运营的技术作为授课的内容,对于学员而言学到的都是一手的真实案例和实际经验,知识内容也可以细化深入。 而且一个产品就可以涵盖公司多个学科的技术,衍生的课程价值辐射多个学科。这可以作为公司的一个核心竞争力。

2. 产品定位

一款汇集科技资讯、技术文章和问答交流的用户移动终端产品。 用户通过该产品,可以获取最新的科技资讯,发表或学习技术文章,讨论交流技术问题。

3. 项目目标

  1. 研发并上线运营头条产品
  2. 从实际的产品技术中孵化产品经理、Python 人工智能、Python 数据分析、Python Web、测试、运维等课程案例
  3. 构建公司自己的数据仓库和算法模型

3. 产品功能架构

自媒体:又称“个人媒体”,是指私人化、平民化、自主化的传播者, 以现代化、电子化的手段,向不特定的大多数或者特定的单个人传递信息的新媒体的总称。 自媒体平台包括:博客、微博、微信、抖音、百度贴吧、论坛/BBS等网络社区。

在这里插入图片描述
产品主要分为三个前端子产品:

  1. 用户端
    APP,用户可以查看资讯、文章内容,进行问答讨论交流
  2. 自媒体运营平台
    PC网站,自媒体用户可以管理文章、评论,查看分析粉丝数据
  3. 系统后台
    PC网站,内部运营管理系统

产品后端系统功能可分为以下几个部分:

  1. 推荐系统部分
    负责为用户个性化推荐资讯和文章
  2. 人工智能部分
    机器自动审核文章、文章画像提取、机器学习推荐算法等
  3. 日志系统部分
    收集保存用户行为数据和系统运行状态数据
  4. 爬虫部分
    爬取网站资讯文章数据,作为产品启动的初期数据来源

4. 产品技术架构

在这里插入图片描述

5. 负载均衡

负载均衡器: 负载均衡(Load Balance)可以将工作任务分摊到多个处理单元, 从而提高并发处理能力
负载均衡器建立在现有网络结构上, 使用它可以实现网络设备的带宽, 增加吞吐量, 加强网络数据处理能力, 提高网络的灵活性和可用性

负载均衡器在工作当中, 要么使用nginx(软件负载均衡), 要么使用F5(硬件负载均衡), 如果使用的是阿里云或者是腾讯云, SLB负载均衡, nginx既可以做负载均衡, 也可以做web前端的中间件

6. 消息队列

消息队列(Message Queue, MQ): 是在消息传输过程中保存消息的容器
消息队列中间件是分布式系统中重要的组件, 主要解决应用解耦, 异步消息, 流量削峰等问题, 实现高性能, 高可用, 可伸缩和最终一致性架构

目前使用较多的消息队列有: Kafka, ActiveMQ, RabbitMQ, ZeroMQ, MetaMQ, RocketMQ

应用场景

  • 异步处理
    将业务逻辑处理由串行方式变成并行方式(好友推荐, 新闻推荐)
  • 应用解耦
    订单系统 --> 库存系统
    发送短信验证码
  • 流量削峰
    秒杀, 抢购活动(用户访问量过大, 导致流量暴增, 应用挂掉)
  • 日志处理
    将消息队列用早日志处理中, 解决大量日志传输的问题

7. 用例设计

1. 编写自动化测试用例的原则

  • 自动化用例一般只实现核心业务流程或者重复执行率较高的功能
  • 自动化测试用例的选择一般以"正向"逻辑的验证为主
  • 不是所有的手工用例都可以使用自动化测试来执行
  • 尽量减少多个用例脚本之间的依赖
  • 自动化测试用例执行完毕后, 一般需要回到原点

2. 编写测试用例

1. 自媒体自媒体
2. 后台管理系统在这里插入图片描述
3. APP在这里插入图片描述

8. 自动化框架结构设计

1. 初始化项目

1. 新建项目

项目名称: uiAutoTestHmtt

2. 创建目录结构
  • uiAutoTestHmtt #项目名称
  • base #封装PO基类
  • page #封装PO页面对象
  • script #定义测试用例脚本
  • data #存放测试数据
  • report #存放生成的测试报告
  • log #存放测试文件
  • screenshot #存放截图
  • config.py #定义项目配置信息
  • utils.py #定义工具类
  • pytest.ini #pytest配置文件
  • conftest #pytest.ini运行配置
3. 安装依赖包
  • selenium包
  • Appium-Python-Client包
  • pytest包
  • pytest-ording包
  • allure-pytest包

2. 初始化代码

1. 封装工具类
  • 封装PO类, 定义BasePage和BaseHandle
    • pytest.ini
[pytest]
addopts = -s -v --html=report/report.html --reruns 3
testpaths = ./uiAutoTestHmtt/script
python_files = test_*.py
python_classes = Test*
python_functions = test_*
  • utils.py
from selenium import webdriver
from appium import webdriver as appdriver

class UtilsDriver:
	_mp_driver = None #表示的是自媒体平台的驱动
	_mis_driver = None #表示的是后台管理驱动
	_app_driver =None # 表示的是app的驱动
	
	# 定义获取自媒体平台的浏览器驱动
	@classmethod
	def get_mp_driver(cls):
		if cls._mp_driver is None:
			cls._mp_driver = webdriver.Chorm()
			cls._mp_driver.get("http://ttmp.research.itcast.cn/")
			cls._mp_driver.maximize_window()
		return cls._mp_driver

	# 定义退出自媒体平台浏览器驱动
	@classmethod
	def quit_mp_driver(cls):
		if cls._mp_driver is not None:
			cls.get_mp_qdriver().quit()
			cls._mp_driver = None
	
	# 定义获取后台管理的驱动
	@classmethod
	def get_mis_driver(cls):
		if cls._mis_driver is None:
			cls._mis_driver = webdriver.Chrom()
			cls._mis_driver.maximize_window()
			cls._mis_driver.get("http://ttmis.research.itcast.cn/")
		return cls._mis_driver

	# 定义退出后台管理系统操作方法
	@classmethod
	def quit_mis_driver(cls):
		if cls._mis_driver is not None:
			cls.get_mis_driver().quit()
			cls._mis_driver = None

	# 定义获取app驱动
	@classmethod
	def get_app_driver(cls):
		if cls._app_driver is None:
			des_cap = {
				"platformName": "android",
				"platformVersion": "5.1.1",
				"deviceName": "emulator-5554",
				"appPackage": "com.itcast.toutiaoApp",
				"appActivity": ".MainActivity",
				"noReset": True, #用来记住app的session
				"resetKeyboard": True,  #重置设备输入键盘
				"unicodeKeyboard": True # 采用unicode编码输入
			}
			cls._app_driver = appdriver.Romate("http://127.0.0.1:4723/wd/hub", des_cap)
			return cls._app_driver

	# 退出app浏览器驱动
	@classmethod
	def quit_app_driver(cls):
	if cls._app_driver is not None:
		cls.get_app_driver().quit()
		cls._app_driver = None
基类的封装
  1. 媒体平台的封装
    base包 -> 创建mp包 -> 在mp包下创建base.py文件
	from selenium.webdriver.support.wait import WebDriverWait

	from utils import UtilsDriver


	# 定义自媒体平台的基类
	# 对象库层基类的封装
	class BasePage:
		def __init__(self):
			self.driver = UtilsDriver.get_mp_driver()  # 获取自媒体浏览器驱动
		
		def get_element(self, location):
		wait = WebDriverWait(self.driver, 10, 1)
		element = wait.until(lambda x: x.find_element(*location))
		return element
	
	# 操作层基类的封装
	class BaseHandle:
	def input_text(self, element, text):
		element.clear()
		element.send_keys(text)
  1. 后台管理基类封装
    base包 -> 创建mis包 -> 在mis包下创建base.py文件
from selenium import webdriver

from utils import UtilsDriver

	# 定义后台管理的基类
	# 页面对象层封装
	class BasePage:
	def __init__(self):
		self.driver = UtilsDriver.get_mis_driver()  # 获取后台管理驱动
	
	def get_element(self, location)
	wait = WebDriverWait(self.driver, 10, 1)
	element = wait.until(lambda x: x.find_element(*location))
	return element
	
	# 操作层的封装
	class BaseHandle:
		def input_text(self, element, text):
			element.clear()
			element.send_keys(text)
  1. app基类封装
    base包 -> 创建app包 -> 在app包下创建base.py文件
from selenium import webdriver

from utils import UtilsDriver
	# 定义app平台的基类
	# 对象库层基类封装
	class BasePage:
		def __init__(self):
			self.driver = UtilsDriver.get_app_driver()   # 获取app浏览器驱动

		def get_element(self, location):
			wait = WebDriverWait(self.driver, 10, 1)
			element = wait.until(lambda x: x.find_element(*location))
			return element
	
	# 操作层基类封装	
	class BaseHandle:
		def input_text(self, element, text):
			element.clear()
			element.send_keys(text)
自媒体平台的封装
自媒体登录页面的封装

page包 -> 新建mp包 -> 创建登录界面login_page.py

from base.mp.base import BasePage, BaseHandle

from selenium.webdriver.common.by import By

# 定义对象库层
class LoginPage(BasePage):
	def __init__(self):
		super().__init__()
		# 手机号码输入框
		self.mobile = By.XPATH, "//*[@placeholder='请输入手机号']"
		# 验证码输入框
		self.code = By.XPATH, "//*[@placeholder='验证码']"
		# 登录按钮
		self.login_btn = By.CSS_SELECTOR, ".el-button--primary"

		# 定位手机号码输入框
		def find_mobile(self):
			return self.get_element(self.mobile)
		
		# 定位验证码输入框
		def find_code(self):
			return self.get_element(self.code)

		# 定位登录按钮
		def find_login_btn(self):
			return self.get_element(self.login_btn)


# 定义操作层
class LoginHandle(BaseHandle):
def __init__(self):
	self.login_page = LoginPage()

	# 输入手机号码
	def input_mobile(self, mobile):
		self.input_text(self.login_page.find_mobile(), mobile)
	
	# 输入验证码
	def input_code(self, code):
		self.input_text(self.login_page.find_code(), code)

	# 点击登录按钮
	def click_login_btn(self):
		self.login_page.find_login_btn().click()


# 定义业务层
class LoginProxy:
	def __init__(self):
		self.login_handle = LoginHandle()
		
	def login(self, mobile, code):
		self.login_page.input_mobile(mobile)  # 输入手机号码
		self.login_page.input_code(code)  # 输入验证码
		self.login_page.click_login_btn()  # 点击登录按钮
自媒体平台首页封装

page包 -> 新建mp包 -> 创建登录界面home_page.py

from selenium.webdriver.common.by import By

from base.mp.base import BasePage, BaseHandle

# 定义对象层
class HomePage(BasePage):
	def __init__(self):
		super().__init__()
		# 用户名显示元素
		self.username = By.CSS_SELECTOR, ".user-name"
		# 内容管理菜单
		self.content_manage = By.XPATH, "//*[text()='内容管理']"
		# 发布文章
		self.public_btn = By.XPATH, "//*[@class='sidebar-el-menu el-menu']/div[2]/li/ul/li[1]"
	
	# 定位用户名显示元素
	def find_username(self):
		return self.get_element(self.username)
	
	# 定位内容管理菜单
	def find_content_manage(self):
		return self.get_element(self.content_manage)

	# 定位发布文章菜单
	def find_publish(self):
		return self.get_element(self.publish_btn)

#定义操作层
class HomeHandle(BaseHandle):
	def __init__(self):
		self.home_page = HomePage()
	
	# 获取用户名信息
	def get_username(self):
		return self.home_page.find_username().text
	
	# 点击内容管理菜单
	def click_content_manage(self):
		self.home_page.find_content_manage().click()

	# 点击发布文章
	def click_publish_btn(self):
		self.home_page.find_publish().click()

# 定义业务层
class HomeProxy:
	def __init__(self):
		self.home_handle = HomeHandle()

	# 获取用户名信息
	def get_username_msg(self):
		return self.home_handle.get_username()

	# 跳转到发布文章页面
	def go_publish_page(self):
		self.home_handle.click_content_manage()
		self.home_handle.click_publish_btn()
自媒体平台发布文章页面封装

page包 -> 新建mp包 -> 创建登录界面publish_page.py

from base.mp.base import BasePage, BaseHandle

from selenium.webdriver.common.by import By

# 定义对象库层
class PublicPage(BasePage):
	def __init__(self):
		super().__init__()
		#文章名称输入框
		self.title = By.XPATH, "//*[@placeholder='文章名称']"
		# iframe元素对象
		self.iframe_ele = By.ID, "publishTinymce_ifr"
		# 文章内容输入框
		self.content = By.CSS_SELECTOR, ".mce-content-body"
		# 封面选择
		self.cover = By.XPATH, "//*[@role=radiogroup]/label[4]/span[2]"
		# 频道选择
		self.channel = By.XPATH, "//*[@placeholder='请选择']"
		# 发表按钮
		self.publish_btn = By.CSS_SELECTOR, "[class='el-button filter-item el-button--primary']"

		# 寻找文章标题
		def find_title(self):
			return self.get_element(self.title)
		# 找到切换的iframe
		def find_iframe_ele(self):
			return self.get_element(self.iframe_ele)
		# 找到文章内容输入框
		def find_content(self):
			return self.get_element(self.content)
		# 找到封面选择输入框
		def find_cover(self):
			return self.get_element(self.cover)
		# 定位频道选择输入框
		def find_channel(self):
			return self.get_element(self.channel)
		# 定位发表按钮
		def find_publish_btn(self):
			return self.get_element(self.publish_btn)

# 操作层
class PublishHandle(BaseHandle):
	def __init__(self):
		self.publish_page = PublishPage()
		self.driver = UtilsDriver.get_mp_driver()

	# 输入文章标题
	def input_title(self, title)
		self.input_text(self.publish_page.find_title(), title)

	# 输入文章内容
	def input_content(self)
		# 切换到iframe中
		self.driver.switch_to.frame(self.publish_page.find_iframe())
		# 输入文章内容
		self.input_text(self.publish_page.find_input_content(), content)
		# 切回默认首页
		self.driver.switch_to.default_content()
	
	# 选择封面
	def choice_cover(self):
		self.publish_page.find_cover().click()
	
	#选择频道
	def choice_channel(self):
		choice_channel(self.driver, self.publish_page.find_channel_choice(), channel)
	
	#点击发布按钮
	def click_publish_btn(self):
		self.publish_page.find_publish_btn().click()

# 业务层封装
class PublishProxy:
	def __init__(self):
		self.publish_handle = PublishHandle()
	
	# 发布文章
	def publish_article(self, title, content, channel):
		# 输入文章标题
		self.publish_handle.input_title(title)
		self.publish_handle.input_content(content)
		self.publish_handle.choice_cover()
		self.publish_handle.choice_channel(channel)
		self.publish_handle.click_publish_btn()

在utils模块中封装选择频道的方法

......
def choice_channel(driver, element, channel)
"""
param driver: 浏览器驱动对象
param element: 选择元素
param channel: 要选择的文本内容
"""
element.click()
time.sleep(1)
xpath = "//*[@class='el-select-dropdown__wrap el-scrollbar__wrap']//*[text()='{0}']".format(channel)
driver.find_element(By.XPATH, xpath).click()

在utils中封装一个方法, 判断元素是否存在

......
# 封装一个方法判断元素是否存在
def is_exis(driver, text):
	"""
	param driver: 浏览器驱动对象
	param text: 定位元素的文本内容
	"""
	xpath = "//*[contains(text(), '{0}')]".format(text)
	try:
		time.sleep(2)
		return driver.find_element(By.XPATH, xpath)
	expect Expection as e:
		return False
自媒体测试用例的编写

scripts模块 -> 创建mp模块 -> 创建test_publish_article.py文件

from page.mp.login_page import LoginProxy
from page.mp.home_page import HomeProxy
from page.mp.publish_page import PublishProxy

class TestPublishArticle:
	# 定义类级别的fixture初始化方法
	def setup_class(self):
		self.login_proxy = LoginProxy()
		self.home_proxy = HomeProxy()
		self.publish_proxy = PublishProxy()

	# 定义类级别的fixture销毁方法
	def teardowm_class(self):
		UtilsDriver.quit_mp_driver()

	# 定义登录测试用例
	def test_login(self):
		self.login_proxy.login("18815687257", "246810")
		username = self.home_proxy.get_username_msg()
		assert "test123" == username

	# 定义文章测试用例
	def test_publish_article(self):
		self.home_proxy.go_publish_page()
		self.publish_proxy.publish_article("发布文章_0321_09", "发布文章_0321_09发布文章_0321_09", "数据库")
		assert is_exist(UtilsDriver.get_mp_driver(), "新增文章成功")
后台管理系统页面的封装
后台管理系统登录页面的封装

page包 -> 新建mis包 -> 创建登录界面login_page.py

from selenium.webdriver.commom.by import By
'''页面对象层'''
class LoginPage(BasePage):
	def __init__(self):
		super().__init__()
		self.username = By.Name, "username"
		self.password = By.Name, "password"
		self.login_btn = By.ID, "inp1"
	
	# 定位用户名输入框
	def find_username(self):
		return self.get_element(self.username)

	# 定位密码框
	def find_password(self):
		return self.get_element(self.password)
	
	# 定位登录按钮
	def find_login_btn(self):
		return self,get_element(self.login_btn)

"""操作层"""
class LoginHandle(BaseHandle):
	def __init__(self):
		self.login_page = LoginPage()
	
	# 输入用户名
	def input_usernane(self, username):
		self.input_text(self.login_page.find_username(), username)

	# 输入密码
	def input_password(self, password):
		self.input_text(self.login_page.find_password(), password)

	# 点击登录
	def _click_login_btn(self):
	# 定义JS
	js = "document.getElementById('inp1').removeAttribute('disabled')"
	# 通过execute_script方法执行JS
	self.login_page.driver.execute_script(js)
	# 点击登录按钮
	self.login_page.find_login_btn().click()
		
# 定义业务层
class LoginProxy:
	def __init__(self):
		self.login_handle = LoginHandle()
	
	# 登录业务操作
	def login(self, username, password):
		self.login_handle.input_username(username)
		self.login_handle.input_password(password)
		self.login_handle.click_login_btn()
后台登录系统主页的封装

page包 -> mis包 -> 创建登录界面home_page.py

# 页面对象层
class HomePage(BasePage):
	def __init__(self):
		super().__init__()
		# 用户信息
		self.user_info = By.CSS_SELECTOR, ".user_info.span"
		# 信息管理
		seld.content_manage = By.XPATH, "//*[@class='side_bar']/ul/li[3]/a"
		# 内容审核
		self.content_audit = By.XPATH, "//*[@class='current3']/li[3]/a"

	# 定位用户信息
	def find_user_info(self):
		return self.get_element(self.user_info)

	# 定位信息管理菜单
	def find_content_manage(self):
		return self.get_elememt(self.content_manage)
	
	# 定位内容审核状态
	def find_context_audit(self):
		return self.get_element(self.content_audit)

# 定义操作层
class HomeHandle(BaseHnadle):
	def __init__(self):
		self
  • 5
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值