121 lines
4.2 KiB
Python
121 lines
4.2 KiB
Python
# coding=utf-8
|
||
|
||
from typing import Tuple, Optional, Iterable
|
||
import re
|
||
import lxml.etree
|
||
|
||
|
||
class _Assert:
|
||
def __init__(self, expected: str, actual: str, negater: bool, failure_msg: str=None):
|
||
"""
|
||
:param expected: 期望值
|
||
:param actual: 实际值
|
||
:param negater: 是否取反
|
||
:param failure_msg: 错误信息 (暂未支持自定义错误信息)
|
||
"""
|
||
self.expected = expected
|
||
self.actual = actual
|
||
self.failure_msg = failure_msg
|
||
self.negater = negater
|
||
|
||
def result(self) -> bool:
|
||
"""断言结果判断"""
|
||
pass
|
||
|
||
def custom_failure_msg(self):
|
||
"""错误信息"""
|
||
pass
|
||
|
||
def exec(self) -> Tuple[bool, Optional[str]]:
|
||
if self.result() ^ self.negater: # 异或
|
||
return True, None
|
||
else:
|
||
if hasattr(self, 'custom_failure_msg'):
|
||
return False, self.custom_failure_msg()
|
||
else:
|
||
return False, self.failure_msg
|
||
|
||
|
||
class AssertEqual(_Assert):
|
||
def result(self):
|
||
return self.expected == self.actual
|
||
|
||
def custom_failure_msg(self):
|
||
if not self.negater:
|
||
return '实际值与期望值不相等: [期望值: %s][实际值: %s]' % (self.expected, self.actual)
|
||
if self.negater:
|
||
return '实际值与期望值相等: [期望值: %s][实际值: %s]' % (self.expected, self.actual)
|
||
|
||
|
||
class AssertSubString(_Assert):
|
||
def result(self):
|
||
return self.expected in self.actual
|
||
|
||
def custom_failure_msg(self):
|
||
if not self.negater:
|
||
return '实际值未包含指定期望值: [期望值: %s][实际值: %s]' % (self.expected, self.actual)
|
||
if self.negater:
|
||
return '实际值包含指定期望值: [期望值: %s][实际值: %s]' % (self.expected, self.actual)
|
||
|
||
|
||
class AssertContains(_Assert):
|
||
def result(self):
|
||
res = re.findall(pattern=self.expected, string=self.actual)
|
||
return len(res) > 0
|
||
|
||
def custom_failure_msg(self):
|
||
if not self.negater:
|
||
return '实际值中未匹配到正则: [正则: %s][实际值: %s]' % (self.expected, self.actual)
|
||
if self.negater:
|
||
return '实际值中匹配到正则: [正则: %s][实际值: %s]' % (self.expected, self.actual)
|
||
|
||
|
||
class AssertMatches(_Assert):
|
||
def result(self):
|
||
res = re.fullmatch(pattern=self.expected, string=self.actual)
|
||
return bool(res)
|
||
|
||
def custom_failure_msg(self):
|
||
if not self.negater:
|
||
return '实际值完整未匹配到正则: [正则: %s][实际值: %s]' % (self.expected, self.actual)
|
||
if self.negater:
|
||
return '实际值完整匹配到正则: [正则: %s][实际值: %s]' % (self.expected, self.actual)
|
||
|
||
|
||
class AssertXPath(_Assert):
|
||
|
||
xpath_separator = '|xpathSeparator|'
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
self.xpath = self.expected.split(self.xpath_separator)[0]
|
||
self.text = self.expected.split(self.xpath_separator)[-1]
|
||
self.match_element_count = 0 # xpath匹配到元素数量
|
||
|
||
def result(self):
|
||
html = lxml.etree.HTML(self.actual)
|
||
elements = html.xpath(self.xpath)
|
||
if not isinstance(elements, Iterable):
|
||
return False
|
||
self.match_element_count = len(elements)
|
||
if len(elements) > 1:
|
||
return False
|
||
elif len(elements) == 1:
|
||
element = elements[0]
|
||
if isinstance(element, str):
|
||
return True if self.text == element else False
|
||
else:
|
||
return True if self.text == element.text else False
|
||
else:
|
||
return False
|
||
|
||
def custom_failure_msg(self):
|
||
if not self.negater:
|
||
if self.match_element_count > 1:
|
||
return '指定的XPath路径 %s 匹配到了 %s 个元素,请调整XPath表达式使其只匹配到一个被测元素' % (self.xpath, self.match_element_count)
|
||
if self.match_element_count == 0:
|
||
return '指定的XPath路径 %s 未匹配到任何元素' % self.xpath
|
||
return '未在指定的XPath路径 %s 下找到文本内容 %s' % (self.xpath, self.text)
|
||
if self.negater:
|
||
return '在指定的XPath路径 %s 下找到文本内容 %s' % (self.xpath, self.text)
|