本文主要介绍PO模式的测试用例,PO模式优点及层级间的关系,相关配置及运行
minitest的测试小程序和测试case:minitest-demo
miniprogram-demo
:测试小程序testcase
:测试case,同时也包含文档的测试casetestcase-PO
:Page Object(PO) 模式的测试casePO模式是自动化测试项目开发实践的最佳设计模式之一。通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化,只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性。
优点:
测试用例基类,继承MiniTest类。复写minium.MiniTest类里面的setUpClass、tearDownClass、setUp、tearDown方法
class BaseCase(minium.MiniTest):
"""
初始化Minium实例,测试用例基类
"""
@classmethod
def setUpClass(cls):
super(BaseCase, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(BaseCase, cls).tearDownClass()
def setUp(self):
pass
def tearDown(self):
pass
封装Minium的页面跳转、元素定位等方法
class BaseDef:
"""
封装Minium的页面跳转、元素定位等方法
"""
def __init__(self, mini):
"""
基类初始化 Minium 实例
"""
self.mini = mini
def navigate_to_page(self, route):
"""
跳转到指定页面
"""
self.mini.app.navigate_to(route)
ret = self.wait_page(route)
return ret
def redirect_to_page(self, route):
"""
跳转到指定页面并关闭当前页面
"""
self.mini.app.redirect_to(route)
ret = self.wait_page(route)
return ret
## ...
页面对象层,各页面的元素定位,操作等
封装公用页面基础操作方法,继承BaseDef类。例如hook wx API接口,点击元素,获取回调;hook 原生控件弹窗,点击元素,处理弹窗,获取回调等
class BasePage(base_def.BaseDef):
"""
封装公用页面基础操作方法
"""
def hook_wx_method(self, method, selector):
"""
封装hook wx API接口,获取回调
:param method: API接口
:param selector: 触发元素选择器
:return: 信号量,回调信息
"""
called = threading.Semaphore(0) # 信号量
callback_args = None
def callback(args):
nonlocal callback_args
called.release()
callback_args = args
# hook wx API接口,获取回调后执行callback
self.mini.app.hook_wx_method(method, callback=callback)
# 点击元素
self.mini.page.get_element(selector).tap()
is_called = called.acquire(timeout=10)
# 释放hook
self.mini.app.release_hook_wx_method(method)
return is_called, callback_args
## ...
首页相关元素定位及相关操作等,继承BaseDef类
class HomePage(base_def.BaseDef):
"""
首页相关元素定位及相关操作等
"""
locator = {
'app': '/page/view/navigator[1]/button', # App接口测试
'page': '/page/view/navigator[2]/button', # Page接口测试
'element': '/page/view/navigator[3]/button', # Element接口测试
'native': '/page/view/navigator[4]/button', # Native接口测试
}
# 进入不同接口测试
def interface_page(self, key):
ele = self.mini.page.get_element(self.locator[key])
ele.tap()
ret = False
if key == 'app':
ret = self.wait_page(router.appPage)
elif key == 'page':
ret = self.wait_page(router.pagePage)
elif key == 'element':
ret = self.wait_page(router.elementPage)
elif key == 'native':
ret = self.wait_page(router.nativePage)
return ret
主要负责业务逻辑和数据驱动
app页面测试用例,测试hook调用
class AppTest(BaseCase):
def setUp(self) -> None:
super().setUp()
# 页面跳转
self.HomePage.interface_page("app")
def __init__(self, methodName='runTest'):
"""
初始化AppTest类,初始化基础页、首页类
"""
super(AppTest, self).__init__(methodName)
self.HomePage = home_page.HomePage(self)
self.BasePage = base_page.BasePage(self)
def test_hook_wx_method(self):
"""
hook 小程序方法 getSystemInfo
:return:
"""
is_called, callback_args = self.BasePage.hook_wx_method("getSystemInfo", "#testhook1")
self.assertTrue(is_called, "callback called")
self.assertDictContainsSubset(
{"errMsg": "getSystemInfo:ok"}, callback_args[0], "getSystemInfo"
)
element页面测试用例,测试操作不同组件用例
class ElementTest(BaseCase):
def setUp(self) -> None:
super().setUp()
# 页面跳转
self.HomePage.interface_page("element")
def __init__(self, methodName='runTest'):
"""
初始化ElementTest类,初始化基础页、首页类
"""
super(ElementTest, self).__init__(methodName)
self.HomePage = home_page.HomePage(self)
self.BasePage = base_page.BasePage(self)
def test_move(self):
"""
movable-view 容器拖拽滑动
"""
ele = self.BasePage.get_element_selector("movable-view")
# reset 重置
ele.move_to(0, 0) # 把movable-view复位
time.sleep(2)
rect = ele.rect # 记录初始位置
self.logger.warn(rect)
ele.move_to(20, 100) # 移动到坐标为20, 100的地方
time.sleep(2)
self.assertDictContainsSubset(
{
"left": 20 + rect.x,
"top": 100 + rect.y,
},
ele.rect,
)
## ...
login页面测试用例,测试登录授权用例
class LoginTest(BaseCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
try:
# 清空授权
cls.mini.clear_auth()
except:
pass
def setUp(self) -> None:
super().setUp()
# 页面跳转
self.HomePage.switch_to_tabbar("/pages/mine/mine")
def __init__(self, methodName='runTest'):
"""
初始化登录类,初始化基础页、首页类
"""
super(LoginTest, self).__init__(methodName)
self.HomePage = home_page.HomePage(self)
self.BasePage = base_page.BasePage(self)
def test_wechat_login(self):
"""
微信登录
"""
self.BasePage.get_element_xpath("/page/view/navigator[1]/button").tap()
time.sleep(2)
ret = self.BasePage.wait_page(router.loginPage)
self.assertTrue(ret, "页面跳转")
# hook 页面方法,并获取回调信息
is_called, callback_args = self.BasePage.hook_native_method(
method="getUserProfile",
selector="/page/view/button",
attr_method="allow_get_user_info")
self.assertTrue(is_called, "callback called")
self.assertDictContainsSubset(
{"errMsg": "getUserProfile:ok"}, callback_args[0], "授权用户信息")
## ...
native页面测试用例,测试处理原生控件用例
class NativeTest(BaseCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
try:
# 清空授权
cls.mini.clear_auth()
except:
pass
def setUp(self) -> None:
super().setUp()
# 页面跳转
self.HomePage.interface_page("native")
def __init__(self, methodName='runTest'):
"""
初始化NativeTest类,初始化基础页、首页类
"""
super(NativeTest, self).__init__(methodName)
self.HomePage = home_page.HomePage(self)
self.BasePage = base_page.BasePage(self)
def test_01_show_modal(self):
"""
showModal弹框授权
:return:
"""
is_called, callback_args = self.BasePage.hook_native_method(
method="showModal",
selector="#testModal",
attr_method="handle_modal")
self.assertTrue(is_called, "callback called")
# 小程序API showModal方法回调信息断言
self.assertDictContainsSubset(
{"errMsg": "showModal:ok"}, callback_args[0]
)
## ...
page页面测试用例,测试页面元素定位用例
class PageTest(BaseCase):
def setUp(self) -> None:
super().setUp()
# 页面跳转
self.HomePage.interface_page("page")
def __init__(self, methodName='runTest'):
"""
初始化PageTest类,初始化基础页、首页类
"""
super(PageTest, self).__init__(methodName)
self.HomePage = home_page.HomePage(self)
self.BasePage = base_page.BasePage(self)
def test_custom_element(self):
"""
自定义组件定位
"""
ele = self.BasePage.get_element_custom("mytest>>>test2>>>text")
self.assertEqual("this is test2", ele.inner_text, "元素定位")
## ...
{
"project_path":"D:\\Program Files\\workspace\\miniApplet\\miniprogram-demo",
"dev_tool_path":"D:\\download\\devtools\\微信web开发者工具\\cli.bat",
"platform": "ide",
"outputs": "outputs"
}
配置需要执行的内容和顺序,以及所在的包。
pkg_list
字段说明要执行用例的内容和顺序, pkg_list
是一个数组,每个数组元素是一个匹配规则,会根据 pkg
去匹配包名,找到测试类,然后再根据 case_list
里面的规则去查找测试类的测试用例。可以根据需要编写匹配的粒度。注意匹配规则不是正则表达式,而是通配符
{
"pkg_list": [
{
"case_list": [
"test_*"
],
"pkg": "test.*_test"
}
]
}
// 按照suite.json测试计划执行,输出报告
minitest -c config.json -s suite.json -g
// 执行page_test中的测试用例
minitest -c config.json -m test.page_test -g
// 执行page_test中的测试用例test_custom_element用例
minitest -c config.json -m test.page_test --case test_custom_element