直播信号

From: https://blog.csdn.net/site008/article/details/77622472
前言
(python基础比较弱的,建议大家多花点时间把基础语法学好,这里有套视频,可以照着练习下:http://pan.baidu.com/s/1i44jZdb 密码:92fs)
熟悉java的应该都清楚常见的单元测试框架Junit和TestNG,这个招聘的需求上也是经常见到的。python里面也有单元测试框架-unittest,相当于是一个python版的junit。
python里面的单元测试框架除了unittest,还有一个pytest框架,这个用的比较少,后面有空再继续分享。
1).先导入unittest
2).用help函数查看源码解析
3).查看描述:
Python unit testing framework, based on Erich Gamma's JUnit and KentBeck's Smalltalk testing framework.
翻译:python的单元测试框架,是基于java的junit测试框架。
1).可以把上图的这段代码copy出来,单独运行,看看测试结果。
Simple usage:
import unittest
class IntegerArithmeticTestCase(unittest.TestCase):
deftestAdd(self): ## test method names begin 'test*'
self.assertEqual((1 + 2), 3)
self.assertEqual(0 + 1, 1)
deftestMultiply(self):
self.assertEqual((0 * 10), 0)
self.assertEqual((5 * 8), 40)
if __name__ == '__main__':
unittest.main()
2).第一行是导入unittest这个模块
3).class这一行是定义一个测试的类,并继承unittest.TestCase这个类
4).接下来是定义了两个测试case名称:testAdd和testMultiply
5).注释里面有句话很重要,这个要敲下黑板记笔记了:## test method names begin 'test*'
--翻译:测试用例的名称要以test开头
6).然后是断言assert,这里的断言方法是assertEqual-判断两个是否相等,
这个断言可以是一个也可以是多个
7).if下面的这个unittest.main()是运行主函数,运行后会看到测试结果(跑了两个用例耗时0.000秒,两个用例都通过):
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
1).上面的两个案例是加法和乘法,我们可以写个case试下减法和除法。
2).有很多小伙伴不知道断言怎么写,断言其实就是拿实际结果和期望结果去对比,对比的方法很多,这里只是举的最简单的一个判断相等的方法。
3).最后运行结果,第二个是失败的,失败原因:AssertionError: 3 != 3.5
F.
======================================================================
FAIL: testDivide (__main__.Test)
这里是测试除法
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:/test/web-project/p.py", line 14, in testDivide
self.assertEqual(result, hope)
AssertionError: 3 != 3.5
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=1)
1).setUp:在写测试用例的时候,每次操作其实都是基于打开浏览器输入对应网址这些操作,这个就是执行用例的前置条件。
2).tearDown:执行完用例后,为了不影响下一次用例的执行,一般有个数据还原的过程,这就是执行用例的后置条件。
3).很多人执行完用例,都不去做数据还原,以致于下一个用例执行失败,这就是不喜欢擦屁股的事情,习惯不好。
4).前置和后置都是非必要的条件,如果没有也可以写pass
1).打开博客首页为例,写一个简单的case
2).判断title完全等于期望结果
3).运行通过,下面会有一个绿条显示:1 test passed
# coding=utf-8
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
import time
import unittest
class Blog(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.get("http://www.cnblogs.com/yoyoketang")
def test_blog(self):
time.sleep(3)
result = EC.title_is(u'上海-悠悠 - 博客园')(self.driver)
print result
self.assertTrue(result)
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
unittest.main()
前言
很多初学者在使用unittest框架时候,不清楚用例的执行顺序到底是怎样的。对测试类里面的类和方法分不清楚,不知道什么时候执行,什么时候不执行。
本篇通过最简单案例详细讲解unittest执行顺序。
一、案例分析
1.先定义一个测试类,里面写几个简单的case
# coding:utf-8
import unittest
import time
class Test(unittest.TestCase):
def setUp(self):
print "start!"
def tearDown(self):
time.sleep(1)
print "end!"
def test01(self):
print "执行测试用例01"
def test03(self):
print "执行测试用例03"
def test02(self):
print "执行测试用例02"
def addtest(self):
print "add方法"
if __name__ == "__main__":
unittest.main()黄蜂赛事前瞻
二、执行结果
D: estpython2python.exe D:/test/test01.py
start!
执行测试用例01
end!
start!
执行测试用例02
end!
start!
执行测试用例03
end!
.
----------------------------------------------------------------------
Ran 3 tests in 3.001s
OK
三、结果分析
1.执行顺序:
start!-执行测试用例01-end!
start!-执行测试用例02-end!
start!-执行测试用例03-end!
2.从执行结果可以看出几点:
--先执行的前置setUp,然后执行的用例(test*),最后执行的后置tearDown。
--测试用例(test*)的执行顺序是根据01-02-03执行的,也就是说根据用例名称来顺序执行的。
--addtest(self)这个方法没执行,说明只执行test开头的用例。
四、selenium实例
1.具体实例参考 登录方法(参数化)
# coding:utf-8
from selenium import webdriver
import unittest
import time
class Bolg(unittest.TestCase):
u'''登录博客'''
def setUp(self):
self.driver = webdriver.Firefox()
url = "https://passport.cnblogs.com/user/signin"
self.driver.get(url)
self.driver.implicitly_wait(30)
def login(self, username, psw):
u'''这里写了一个登录的方法,账号和密码参数化'''
self.driver.find_element_by_id("input1").send_keys(username)
self.driver.find_element_by_id("input2").send_keys(psw)
self.driver.find_element_by_id("signin").click()
time.sleep(3)
def is_login_sucess(self):
u'''判断是否获取到登录账户名称'''
try:
text = self.driver.find_element_by_id("lnk_current_user").text
print text
return True
except:
return False
def test_01(self):
u'''登录案例参考:账号,密码自己设置'''
self.login(u"上海-悠悠", u"xxxx") # 调用登录方法
# 判断结果
result = self.is_login_sucess()
self.assertTrue(result)
def test_02(self):
u'''登录案例参考:账号,密码自己设置'''
self.login(u"上海-悠悠", u"xxxx") # 调用登录方法
# 判断结果
result = self.is_login_sucess()
self.assertTrue(result)
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
unittest.main()
我们在写用例的时候,单个脚本的用例好执行,那么多个脚本的时候,如何批量执行呢?这时候就需要用到unittet里面的discover方法来加载用例了。
加载用例后,用unittest里面的TextTestRunner这里类的run方法去一次执行多个脚本的用例。
一、新建测试项目
1.pycharm左上角File>New Projetc>Pure Python,在location位置命名一个测试工程的名称:yoyotest,然后保存
2.选中刚才新建的工程右键>New>Python Package>新建一个case文件夹
3.重复第2步的操作,新建一个case的文件夹,在里面添加一个baidu和一个blog的文件夹,里面分别有两个用例的脚本,如下图所示。
test_01,test_02,test_03,test_04是我们写用例的脚本
4.test_01创建完后,打开脚本,写入用例
5.在yoyotest这个项目下面创建一个脚本run_all_case.py,接下来用这个脚本去批量执行所有的用例。
二、diascover加载测试用例
1.discover方法里面有三个参数:
-case_dir:这个是待执行用例的目录。
-pattern:这个是匹配脚本名称的规则,test*.py意思是匹配test开头的所有脚本。
-top_level_dir:这个是顶层目录的名称,一般默认等于None就行了。
2.discover加载到的用例是一个list集合,需要重新写入到一个list对象testcase里,这样就可以用unittest里面的TextTestRunner这里类的run方法去执行。
3.运行后结果如下,就是加载到的所有测试用例了:
testMethod=test01>, testMethod=test01>, testMethod=test01>, testMethod=test01>, 三、run测试用例 1.为了更方便的理解,可以把上面discover加载用例的方法封装下,写成一个函数 2.参考代码: # coding:utf-8 import unittest import os # 用例路径 case_path = os.path.join(os.getcwd(), "case") # 报告存放路径 report_path = os.path.join(os.getcwd(), "report") def all_case(): discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py", top_level_dir=None) print(discover) return discover if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(all_case()) 前言 前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量,但是有个弊端,比如打开浏览器操作,每次执行用例时候都会重新打开,这样就会浪费很多时间。 于是就想是不是可以只打开一次浏览器,执行完用例再关闭呢?这就需要用到装饰器(@classmethod)来解决了。 一、装饰器 1.用setUp与setUpClass区别 setup():每个测试case运行前运行 teardown():每个测试case运行完后执行 setUpClass():必须使用@classmethod 装饰器,所有case运行前只运行一次 tearDownClass():必须使用@classmethod装饰器,所有case运行完后只运行一次 2.@是修饰符,classmethod是python里的类方法 二、执行顺序 1.用类方法写几个简单case,可以对比这篇:Selenium2+python自动化52-unittest执行顺序 # coding:utf-8 import unittest import time class Test(unittest.TestCase): @classmethod def setUpClass(cls): print "start!" @classmethod def tearDownClass(cls): time.sleep(1) print "end!" def test01(self): print "执行测试用例01" def test03(self): print "执行测试用例03" def test02(self): print "执行测试用例02" def addtest(self): print "add方法" if __name__ == "__main__": unittest.main() 2.从执行结果可以看出,前置和后置在执行用例前只执行了一次。 start! 执行测试用例01 执行测试用例02 执行测试用例03 ...end! ---------------------------------------------------------------------- Ran 3 tests in 1.001s 三、selenium实例 1.可以把打开浏览器操作放到前置setUpClass(cls)里,这样就可以实现打开一次浏览器,执行多个case了 # coding:utf-8 from selenium import webdriver from selenium.webdriver.support import expected_conditions as EC import unittest class BolgHome(unittest.TestCase): u'''博客首页''' @classmethod def setUpClass(cls): cls.driver = webdriver.Firefox() url = "http://www.cnblogs.com/yoyoketang/" cls.driver.get(url) cls.driver.implicitly_wait(30) @classmethod def tearDownClass(cls): cls.driver.quit() def test_01(self): u'''验证元素存在:博客园''' locator = ("id", "blog_nav_sitehome") text = u"博客园" result = EC.text_to_be_present_in_element(locator, text)(self.driver) self.assertTrue(result) def test_02(self): u'''验证元素存在:首页''' locator = ("id", "blog_nav_myhome") text = u"首页" result = EC.text_to_be_present_in_element(locator, text)(self.driver) self.assertTrue(result) if __name__ == "__main__": unittest.main() 前言 批量执行完用例后,生成的测试报告是文本形式的,不够直观,为了更好的展示测试报告,最好是生成HTML格式的。 unittest里面是不能生成html格式报告的,需要导入一个第三方的模块:HTMLTestRunner 备注:(以下是python2.7的HTMLTestRunner,python3.x的HTMLTestRunner需要自己稍做修改,可以在这里下载:http://pan.baidu.com/s/1hs5OXNY) 一、导入HTMLTestRunner 1.这个模块下载不能通过pip安装了,只能下载后手动导入,下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html 2.Download下HTMLTestRunner.py文件就是我们需要下载的包。 3.下载后手动拖到python安装文件的Lib目录下 二、demo解析 1.下载Download下的第二个文件test_HTMLTestRunner.py,这个就是官方给的一个测试demo了,从这个文件可以找到该模块的用法。 2.找到下图这段,就是官方给的一个demo了,test_main()里上半部分就是加载测试case,我们不需要搞这么复杂。 参考前面一篇内容就行了Selenium2+python自动化53-unittest批量执行(discover) 3.最核心的代码是下面的红色区域,这个就是本篇的重点啦。 三、生成html报告 1.我们只需把上面红色区域代码copy到上一篇的基础上稍做修改就可以了,这里主要有三个参数: --stream:测试报告写入文件的存储区域 --title:测试报告的主题 --description:测试报告的描述 2.report_path是存放测试报告的地址 四、测试报告详情 1.找到测试报告文件,用浏览器打开,点开View里的Detail可以查看详情描述。 2.为了生成带中文描述的测试用例,可以在case中添加注释,如在test_01的脚本添加如下注释: class Test(unittest.TestCase): def setUp(self): print "start!" def tearDown(self): time.sleep(1) print "end!" def test01(self): u'''测试登录用例,账号:xx 密码xx''' print "执行测试用例01" def test03(self): u'''测试登搜索用例,关键词:xxx''' print "执行测试用例03" 3.重新运行后查看测试报告 五、参考代码: 1.我下面的代码文件路径用的相对路径,这样就避免代码换个地址找不到路径的情况了 # coding:utf-8 import unittest import os import HTMLTestRunner # 用例路径 case_path = os.path.join(os.getcwd(), "case") # 报告存放路径 report_path = os.path.join(os.getcwd(), "report") def all_case(): discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py", top_level_dir=None) print(discover) return discover if __name__ == "__main__": # runner = unittest.TextTestRunner() # runner.run(all_case()) # html报告文件路径 report_abspath = os.path.join(report_path, "result.html") fp = open(report_abspath, "wb") runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u'自动化测试报告,测试结果如下:', description=u'用例执行情况:') # 调用add_case函数返回值 runner.run(all_case()) fp.close() 前言 python2用HTMLTestRunner生成测试报告时,有中文输出情况会出现乱码,这个主要是编码格式不统一,改下编码格式就行。 下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html 一、中文乱码 1.测试报告中,msg自定义异常内容有中文情况会出现乱码,如下图所示 二、修改编码 1.找到HTMLTestRunner.py文件,搜索:uo = 2.找到红色区域设置编码的两个地方 3.注释掉红色区域这两个设置,重新添加编码格式为:uo = o.decode('utf-8') ue = e.decode('utf-8') 4.修改好之后记得保存,重新运行,乱码问题就解决了 三、python3报告问题 1.python3的小伙伴直接用这个下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html的文件,是不能直接生成报告的,需要稍做修改 2.修改后的源文件已经打包:https://files.cnblogs.com/files/zidonghua/HTMLTestRunner%28%E7%8B%AC%E5%AE%B6%E5%90%88%E9%9B%86%29.zip (另外很多朋友在简单的示例代码中,生成不了测试报告,我来上传一份完整项目模板:https://files.cnblogs.com/files/zidonghua/%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E6%90%9E%E4%B8%8D%E5%87%BA%E6%9D%A5%E7%9A%84%E7%9C%8B%E8%BF%99%E9%87%8C.rar) 前言 在测试用例中,执行完测试用例后,最后一步是判断测试结果是pass还是fail,自动化测试脚本里面一般把这种生成测试结果的方法称为断言(assert)。 用unittest组件测试用例的时候,断言的方法还是很多的,下面介绍几种常用的断言方法:assertEqual、assertIn、assertTrue 1).下面写了4个case,其中第四个是执行失败的 # coding:utf-8 import unittest class Test(unittest.TestCase): def test01(self): '''判断 a == b ''' a = 1 b = 1 self.assertEqual(a, b) def test02(self): '''判断 a in b''' a = "hello" b = "hello world!" self.assertIn(a, b) def test03(self): '''判断 a isTrue ''' a = True self.assertTrue(a) def test04(self): '''失败案例''' a = "上海-悠悠" b = "yoyo" self.assertEqual(a, b) if __name__ == "__main__": unittest.main() 2).执行结果如下 Failure Expected :'xe4xb8x8axe6xb5xb7-xe6x82xa0xe6x82xa0' Actual :'yoyo' Traceback (most recent call last): File "D: estyoyotestkecheng est12.py", line 27, in test04 self.assertEqual(a, b) AssertionError: 'xe4xb8x8axe6xb5xb7-xe6x82xa0xe6x82xa0' != 'yoyo' 3.执行的结果,中文编码不对,没正常显示中文,遇到这种情况,可以自定义异常输出 1).以assertEqual为例分析: assertEqual(self, first, second, msg=None) Fail if the two objects are unequal as determined by the'==' operator. 2).翻译:如果两个对象不能相等,就返回失败,相当于return: first==second 3).这里除了相比较的两个参数first和second,还有第三个参数msg=None,这个msg参数就是遇到异常后自定义输出信息 1).assertEqual(self, first, second,msg=None) --判断两个参数相等:first == second 2).assertNotEqual(self, first, second,msg=None) --判断两个参数不相等:first != second 3).assertIn(self, member, container,msg=None) --判断是字符串是否包含:member in container 4).assertNotIn(self, member,container, msg=None) --判断是字符串是否不包含:member not in container 5).assertTrue(self, expr, msg=None) --判断是否为真:expr is True 6).assertFalse(self, expr, msg=None) --判断是否为假:expr is False 7).assertIsNone(self, obj, msg=None) --判断是否为None:objis None 8).assertIsNotNone(self, obj,msg=None) --判断是否不为None:obj is not None 3.7.4 unittest所有断言方法 1).下面是unittest框架支持的所有断言方法,有兴趣的同学可以慢慢看。(官方资料) | assertAlmostEqual(self, first, second, places=None, msg=None,delta=None) | Fail if the two objects are unequal asdetermined by their | difference rounded to the given number ofdecimal places | (default 7) and comparing to zero, or bycomparing that the | between the two objects is more than the givendelta. | | Note that decimal places (from zero) areusually not the same | as significant digits (measured from the mostsignficant digit). | | If the two objects compare equal then they willautomatically | compare almost equal. | | assertAlmostEquals = assertAlmostEqual(self, first, second,places=None, msg=None, delta=None) | | assertDictContainsSubset(self, expected, actual, msg=None) | Checks whether actual is a superset ofexpected. | | assertDictEqual(self, d1, d2, msg=None) | | assertEqual(self, first, second, msg=None) | Fail if the two objects are unequal asdetermined by the '==' | operator. | | assertEquals = assertEqual(self, first, second, msg=None) | | assertFalse(self, expr, msg=None) | Check that the expression is false. | | assertGreater(self, a, b, msg=None) | Just like self.assertTrue(a > b), but with anicer default message. | | assertGreaterEqual(self, a, b, msg=None) | Just like self.assertTrue(a >= b), but witha nicer default message. | | assertIn(self, member, container, msg=None) | Just like self.assertTrue(a in b), but with anicer default message. | | assertIs(self, expr1, expr2, msg=None) | Just like self.assertTrue(a is b), but with anicer default message. | | assertIsInstance(self, obj, cls, msg=None) | Same as self.assertTrue(isinstance(obj, cls)),with a nicer | default message. | | assertIsNone(self, obj, msg=None) | Same as self.assertTrue(obj is None), with anicer default message. | | assertIsNot(self, expr1, expr2, msg=None) | Just like self.assertTrue(a is not b), but witha nicer default message. | | assertIsNotNone(self, obj, msg=None) | Included for symmetry with assertIsNone. | | assertItemsEqual(self, expected_seq, actual_seq, msg=None) | An unordered sequence specific comparison. Itasserts that | actual_seq and expected_seq have the sameelement counts. | Equivalent to:: | | self.assertEqual(Counter(iter(actual_seq)), | Counter(iter(expected_seq))) | | Asserts that each element has the same count inboth sequences. | Example: | - [0, 1, 1] and [1, 0,1] compare equal. | - [0, 0, 1] and [0, 1]compare unequal. | | assertLess(self, a, b, msg=None) | Just like self.assertTrue(a < b), but with anicer default message. | | assertLessEqual(self, a, b, msg=None) | Just like self.assertTrue(a <= b), but witha nicer default message. | | assertListEqual(self, list1, list2, msg=None) | A list-specific equality assertion. | | Args: | list1: The first listto compare. | list2: The second listto compare. | msg: Optional messageto use on failure instead of a list of | differences. | | assertMultiLineEqual(self, first, second, msg=None) | Assert that two multi-line strings are equal. | | assertNotAlmostEqual(self, first, second, places=None, msg=None,delta=None) | Fail if the two objects are equal as determinedby their | difference rounded to the given number ofdecimal places | (default 7) and comparing to zero, or bycomparing that the | between the two objects is less than the givendelta. | | Note that decimal places (from zero) areusually not the same | as significant digits (measured from the mostsignficant digit). | | Objects that are equal automatically fail. | | assertNotAlmostEquals = assertNotAlmostEqual(self, first, second, places=None,msg=None, delta=None) | | assertNotEqual(self, first, second, msg=None) | Fail if the two objects are equal as determinedby the '!=' | operator. | | assertNotEquals = assertNotEqual(self, first, second, msg=None) | | assertNotIn(self, member, container, msg=None) | Just like self.assertTrue(a not in b), but witha nicer default message. | | assertNotIsInstance(self, obj, cls, msg=None) | Included for symmetry with assertIsInstance. | | assertNotRegexpMatches(self, text, unexpected_regexp, msg=None) | Fail the test if the text matches the regularexpression. | | assertRaises(self, excClass, callableObj=None, *args, **kwargs) | Fail unless an exception of class excClass israised | by callableObj when invoked with arguments argsand keyword | arguments kwargs. If a different type ofexception is | raised, it will not be caught, and the testcase will be | deemed to have suffered an error, exactly asfor an | unexpected exception. | | If called with callableObj omitted or None,will return a | context object used like this:: | | withself.assertRaises(SomeException): | do_something() | | The context manager keeps a reference to theexception as | the 'exception' attribute. This allows you toinspect the | exception after the assertion:: | | withself.assertRaises(SomeException) as cm: | do_something() | the_exception =cm.exception | self.assertEqual(the_exception.error_code, 3) | | assertRaisesRegexp(self, expected_exception, expected_regexp,callable_obj=None, *args, **kwargs) | Asserts that the message in a raised exceptionmatches a regexp. | | Args: | expected_exception:Exception class expected to be raised. | expected_regexp: Regexp(re pattern object or string) expected | to be found in error message. | callable_obj: Functionto be called. | args: Extra args. | kwargs: Extra kwargs. | | assertRegexpMatches(self, text, expected_regexp, msg=None) | Fail the test unless the text matches theregular expression. | | assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None) | An equality assertion for ordered sequences(like lists and tuples). | | For the purposes of this function, a validordered sequence type is one one | which can be indexed, has a length, and has anequality operator. | | Args: | seq1: The firstsequence to compare. | seq2: The secondsequence to compare. | seq_type: The expecteddatatype of the sequences, or None if no | datatype should be enforced. | msg: Optional messageto use on failure instead of a list of | differences. | | assertSetEqual(self, set1, set2, msg=None) | A set-specific equality assertion. | | Args: | set1: The first set tocompare. | set2: The second set tocompare. | msg: Optional messageto use on failure instead of a list of | differences. | | assertSetEqual uses ducktyping to supportdifferent types of sets, and | is optimized for sets specifically (parametersmust support a | difference method). | | assertTrue(self, expr, msg=None) | Check that the expression is true. | | assertTupleEqual(self, tuple1, tuple2, msg=None) | A tuple-specific equality assertion. | | Args: | tuple1: The first tupleto compare. | tuple2: The secondtuple to compare. | msg: Optional messageto use on failure instead of a list of | differences. 前言 到unittest这里基本上可以搭建一个简易的项目框架了,我们可以用一条run_main.py脚本去控制执行所有的用例,并生成报告,发送邮件一系列的动作 一、新建工程 1.打开pycharm左上角File>New Project,在Location位置输入项目名称:D: est est_blog 2.创建之后,选择Opin in current window就可以了 二、项目结构 1.在测试工程下,创建文件夹,一定要选Python Package的方式创建,要不然后面导入自己写的模块会出现各种问题 2.在工程下创建以下几个文件 --test_case 这个文件夹放所有测试用例 ----blog_home 可以按功能用例模块划分 ---------test_home ---------test_home_1 测试用例以test开头命名 ----blog_login ---------test_login ----blog_set ---------test_set --test_report --run_main.py 注意这个脚本放文件根目录 三、run_main 1.run_main.py这个脚本里面写主函数,控制执行所有的用例,最终我们只需要运行这个脚本就可以了 2.我们也可以在cmd里执行这个脚本文件,这样就不用依赖pycharm去执行了(后续用jenkins执行,也是同样道理,启动cmd执行脚本) >>d: >>cd test est_blog >>python run_main.py 3.run_main.py源代码在下一章节。 生成测试项目报告模板: https://files.cnblogs.com/files/zidonghua/%E7%94%9F%E6%88%90%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E9%A1%B9%E7%9B%AE%E6%A8%A1%E6%9D%BF.zip 测试报告搞不出来的看这里: https://files.cnblogs.com/files/zidonghua/%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E6%90%9E%E4%B8%8D%E5%87%BA%E6%9D%A5%E7%9A%84%E7%9C%8B%E8%BF%99%E9%87%8C2.rar 以下代码在python2和python3上都跑通过,python3只需注释掉上面红色框框区域代码就行(最后一步发送邮箱代码,我注释掉了)。 # coding=utf-8 import unittest import time import HTMLTestRunner from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import smtplib import os ####下面三行代码python2报告出现乱码时候可以加上#### import sys reload(sys) sys.setdefaultencoding('utf8') # 这个是优化版执行所有用例并发送报告,分四个步骤 # 第一步加载用例 # 第二步执行用例 # 第三步获取最新测试报告 # 第四步发送邮箱 (这一步不想执行的话,可以注释掉最后面那个函数就行) def add_case(case_path, rule): '''加载所有的测试用例''' testunit = unittest.TestSuite() # 定义discover方法的参数 discover = unittest.defaultTestLoader.discover(case_path, pattern=rule, top_level_dir=None) # discover方法筛选出来的用例,循环添加到测试套件中 # for test_suite in discover: # for test_case in test_suite: # testunit.addTests(test_case) # print testunit testunit.addTests(discover) # 直接加载discover print(testunit) return testunit def run_case(all_case, report_path): '''执行所有的用例, 并把结果写入测试报告''' now = time.strftime("%Y_%m_%d %H_%M_%S") report_abspath = os.path.join(report_path, now+"result.html") # report_abspath = "D:\web_project\report\"+now+"result.html" fp = open(report_abspath, "wb") runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u'自动化测试报告,测试结果如下:', description=u'用例执行情况:') # 调用add_case函数返回值 runner.run(all_case) fp.close() def get_report_file(report_path): '''获取最新的测试报告''' lists = os.listdir(report_path) lists.sort(key=lambda fn: os.path.getmtime(os.path.join(report_path, fn))) print (u'最新测试生成的报告: '+lists[-1]) # 找到最新生成的报告文件 report_file = os.path.join(report_path, lists[-1]) return report_file def send_mail(sender, psw, receiver, smtpserver, report_file): '''发送最新的测试报告内容''' # 读取测试报告的内容 with open(report_file, "rb") as f: mail_body = f.read() # 定义邮件内容 msg = MIMEMultipart() body = MIMEText(mail_body, _subtype='html', _charset='utf-8') msg['Subject'] = u"自动化测试报告" msg["from"] = sender msg["to"] = psw # 加上时间戳 # msg["date"] = time.strftime('%a, %d %b %Y %H_%M_%S %z') msg.attach(body) # 添加附件 att = MIMEText(open(report_file, "rb").read(), "base64", "utf-8") att["Content-Type"] = "application/octet-stream" att["Content-Disposition"] = 'attachment; filename= "report.html"' msg.attach(att) # 登录邮箱 smtp = smtplib.SMTP() # 连接邮箱服务器 smtp.connect(smtpserver) # 用户名密码 smtp.login(sender, psw) smtp.sendmail(sender, receiver, msg.as_string()) smtp.quit() print('test report email has send out !')if __name__ == "__main__": # 测试用例的路径、匹配规则 case_path = "D:\test\newp\case" rule = "test*.py" all_case = add_case(case_path, rule) # 1加载用例 # 生成测试报告的路径 report_path = "D:\test\newp\report" run_case(all_case, report_path) # 2执行用例 # 获取最新的测试报告文件 report_file = get_report_file(report_path) # 3获取最新的测试报告 #邮箱配置 sender = "yoyo@xxx.com" psw = "xxx" # 收件人多个时,中间用逗号隔开,如'a@xx.com,b@xx.com' receiver = "yoyo@xxx.com" smtp_server = 'smtp.xxx.com' # send_mail(sender, psw, receiver, smtp_server, report_file) # 4最后一步发送报告,需要发邮件就取消注释。 以登录博客园为案例https://passport.cnblogs.com/user/signin 一、登录方法封装 1.我们可以把登录写成一个登录类,里面写个登录的方法,保存文件为login_pub.py # coding:utf-8 ''' 这里写了一个登录博客园的类,登录博客园方法 ''' class Login_Blog(): '''登录类封装''' def __init__(self, driver): '''初始化driver参数''' self.driver = driver def input_user(self, username): '''输入用户名''' self.driver.find_element_by_id("input1").clear() self.driver.find_element_by_id("input1").send_keys(username) def input_psw(self,psw): '''输入密码''' self.driver.find_element_by_id("input2").clear() self.driver.find_element_by_id("input2").send_keys(psw) def click_button(self): '''点击登录按钮''' self.driver.find_element_by_id("signin").click() def login(self, username, psw): '''登录公共方法''' self.input_user(username) self.input_psw(psw) self.click_button() 2.调用登录公共方法 # coding:utf-8 from selenium import webdriver import unittest from login_pub import Login_Blog login_url = "https://passport.cnblogs.com/user/signin" class TetsLogin(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.get(login_url) def tearDown(self): self.driver.quit() def test_login(self): # 调用登录类里面的login方法 Login_Blog(self.driver).login("xxx", "111") self.driver.find_element() # 后面接着的操作省略了 if __name__ == "__main__": unittest.main() 前言 在定位元素的时候,经常会遇到各种异常,为什么会发生这些异常,遇到异常又该如何处理呢? 本篇通过学习selenium的exceptions模块,了解异常发生的原因。 一、发生异常 1.打开博客首页,定位“新随笔”元素,此元素 2.为了故意让它定位失败,我在元素属性后面加上xx 3.运行失败后如下图所示,程序在查找元素的这一行发生了中断,不会继续执行click事件了 二、捕获异常 1.为了让程序继续执行,我们可以用try...except...捕获异常。捕获异常后可以打印出异常原因,这样以便于分析异常原因。 2.从如下异常内容可以看出,发生异常原因是:NoSuchElementException selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: {"method":"id","selector":"blog_nav_newpostxx"} 3.从selenium.common.exceptions 导入 NoSuchElementException类。 三、参考代码: # coding:utf-8 from selenium import webdriver from selenium.common.exceptions import NoSuchElementException driver = webdriver.Firefox() driver.get("http://www.cnblogs.com/yoyoketang/") # 定位首页"新随笔" try: element = driver.find_element("id", "blog_nav_newpostxx") except NoSuchElementException as msg: print u"查找元素异常%s"%msg # 点击该元素 else: element.click() 四、selenium常见异常 1.NoSuchElementException:没有找到元素 2.NoSuchFrameException:没有找到iframe 3.NoSuchWindowException:没找到窗口句柄handle 4.NoSuchAttributeException:属性错误 5.NoAlertPresentException:没找到alert弹出框 6.ElmentNotVisibleException:元素不可见 7.ElementNotSelectableException:元素没有被选中 8.TimeoutException:查找元素超时 备注:其它异常与源码在Lib目录下:selenium/common/exceptions有兴趣的可以看看。 前言 在执行用例过程中由于是无人值守的,用例运行报错的时候,我们希望能对当前屏幕截图,留下证据。 在写用例的时候,最后一步是断言,可以把截图的动作放在断言这里,那么如何在断言失败后截图呢? 一、截图方法 1.get_screenshot_as_file(self, filename) --这个方法是获取当前window的截图,出现IOError时候返回False,截图成功返回True。 filename参数是保存文件的路径。 Usage: driver.get_screenshot_as_file('/Screenshots/foo.png') 2.get_screenshot_as_base64(self) --这个方法也是获取屏幕截图,保存的是base64的编码格式,在HTML界面输出截图的时候,会用到。 比如,想把截图放到html测试报告里。 Usage: driver.get_screenshot_as_base64() 3.get_screenshot_as_png(self) --这个是获取屏幕截图,保存的是二进制数据,很少用到。 Usage: driver.get_screenshot_as_png() 二、异常后截图 1.为了能抛异常,把定位登录按钮的id换了个错的id。 2.给图片命名时候加个时间戳,避免同一个文件名称被覆盖掉。 3.文件路径,这里直接写的文件名称,就是跟当前的脚本同一个路径。如果图片输出到其它文件路径,需要些文件的绝对路径了。 4.截图的结果,如果没截到图返回False,截图成功会返回True。 三、selenium实例 1.在unittest框架里写用例的时候,我们希望在断言失败的时候,对当前屏幕截图。 2.如果加try...except捕获异常后结果,此时所有的测试用例都是通过的了,会影响测试结果。解决办法其实很简单,再把异常抛出来就行了。 3.参考代码: # coding:utf-8 from selenium import webdriver import time,unittest from selenium.webdriver.support import expected_conditions as EC class Login(unittest.TestCase): def setUp(self): url_login = "https://passport.cnblogs.com/user/signin" self.driver = webdriver.Firefox() self.driver.get(url_login) def test_01(self): '''前面输入账号密码,让正确运行到assert这一步,断言故意设置为Fals e不成功''' try: self.driver.find_element_by_id("input1").send_keys(u"上海-悠悠") self.driver.find_element_by_id("input2").send_keys("xxx") # 登录id是错的,定位会抛异常 self.driver.find_element_by_id("signin").click() # 判断登录成功页面是否有账号:"上海-悠悠" time.sleep(3) locator = ("id", "lnk_current_user") result = EC.text_to_be_present_in_element(locator,u"上海-悠悠")(self.driver) self.assertFalse(result) except Exception as msg: print(u"异常原因%s"%msg) # 图片名称可以加个时间戳 nowTime = time.strftime("%Y%m%d.%H.%M.%S") self.driver.get_screenshot_as_file('%s.jpg' % nowTime) raise def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main() 4.运行结果: 异常原因True is not false Failure Traceback (most recent call last): File "D: estyoyotketang est01.py", line 22, in test_01 self.assertFalse(result) AssertionError: True is not false 前言 本篇总结了QQ邮箱和163邮箱发送邮件,邮件包含html中文和附件,可以发给多个收件人,专治各种不行,总之看完这篇麻麻再也不用担心我的邮件收不到了。 以下代码兼容python2和python3,运行无异常,放心大胆食用。 一、163邮箱 1.先导入smtplib库用来发送邮件,导入MIMEText库用来做纯文本的邮件模板 2.先准备几个跟发邮件相关的参数,每个邮箱的发件服务器都不一样,以163为例,百度搜到发件服务器为:smtp.163.com 3.接下来就是写邮件的主题和正文内容,正文这里用html格式的 4.最后调用SMTP发件服务 5.参考代码: # coding:utf-8 import smtplib from email.mime.text import MIMEText # ----------1.跟发件相关的参数------ smtpserver = "smtp.163.com" # 发件服务器 port = 0 # 端口 sender = "yoyo@163.com" # 账号 psw = "**************" # 密码 receiver = "283340479@qq.com" # 接收人 # ----------2.编辑邮件的内容------ subject = "这个是主题163" body = ' 这个是发送的163邮件 msg = MIMEText(body, "html", "utf-8") msg['from'] = sender msg['to'] = "283340479@qq.com" msg['subject'] = subject # ----------3.发送邮件------ smtp = smtplib.SMTP() smtp.connect(smtpserver) # 连服务器 smtp.login(sender, psw) # 登录 smtp.sendmail(sender, receiver, msg.as_string()) # 发送 smtp.quit() # 关闭 二、QQ邮件 1.QQ邮箱是需要SSL认证的,这种邮箱跟上面的就有点不一样了。 2.找到QQ邮箱授权码,打开QQ邮箱-设置-账号-POP3开启服务-开启 (如果已经开启了,不知道授权码,就点温馨提示里面的‘生成授权码’) 3.发验证短信获取授权码,照着提示发个短信,如何点我已发送,就会收到授权码了。 4.收到授权码后复制,保存下来,这个就可以当QQ邮箱的密码了。 5.QQ邮箱发送邮件代码,跟163有点不一样,如下图红色框框: 6.参考代码: # coding:utf-8 import smtplib from email.mime.text import MIMEText # ----------1.跟发件相关的参数------ # smtpserver = "smtp.163.com" # 发件服务器 smtpserver = "smtp.qq.com" port = 465 # 端口 sender = "283340479@qq.com" # 账号 psw = "**************" # 密码 receiver = "283340479@qq.com" # 接收人 # ----------2.编辑邮件的内容------ subject = "这个是主题QQ" body = ' 这个是发送的QQ邮件 msg = MIMEText(body, "html", "utf-8") msg['from'] = sender msg['to'] = "283340479@qq.com" msg['subject'] = subject # ----------3.发送邮件------ # smtp = smtplib.SMTP() # smtp.connect(smtpserver) # 连服务器 smtp = smtplib.SMTP_SSL(smtpserver, port) smtp.login(sender, psw) # 登录 smtp.sendmail(sender, receiver, msg.as_string()) # 发送 smtp.quit() # 关闭 三、兼容163和QQ邮箱 1.如果想兼容上面两种方式发送邮件,只需把第三块内容稍微改下,如下所示 四、发送带附件 1.上面的MIMEText只能发送正文,无法带附件,发送带附件的需要导入另外一个模块MIMEMultipart 2.先读取要发送文件的内容,file_path是路径的参数名 3.下图红色框框file_name参数是发送的附件重新命名 4.参考代码: # coding:utf-8 import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart # ----------1.跟发件相关的参数------ smtpserver = "smtp.163.com" # 发件服务器 port = 0 # 端口 sender = "yoyo@163.com" # 账号 psw = "***********" # 密码 receiver = "283340479@qq.com" # 接收人 # ----------2.编辑邮件的内容------ # 读文件 file_path = "result.html" with open(file_path, "rb") as fp: mail_body = fp.read() msg = MIMEMultipart() msg["from"] = sender # 发件人 msg["to"] = receiver # 收件人 msg["subject"] = "这个我的主题" # 主题 # 正文 body = MIMEText(mail_body, "html", "utf-8") msg.attach(body) # 附件 att = MIMEText(mail_body, "base64", "utf-8") att["Content-Type"] = "application/octet-stream" att["Content-Disposition"] = 'attachment; filename="test_report.html"' msg.attach(att) # ----------3.发送邮件------ try: smtp = smtplib.SMTP() smtp.connect(smtpserver) # 连服务器 smtp.login(sender, psw) except: smtp = smtplib.SMTP_SSL(smtpserver, port) smtp.login(sender, psw) # 登录 smtp.sendmail(sender, receiver, msg.as_string()) # 发送 smtp.quit() 5.最后结果,有图有真相 五、发给多个收件人 1.上面都是发给一个收件人,那么如何一次发给多个收件人呢?只需改两个小地方 2.把receiver参数改成list对象,单个多个都是可以收到的 3.msg["to"]这个参数不能用list了,得先把receiver参数转化成字符串,如下图所示 4.参考代码: # coding:utf-8 import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart # ----------1.跟发件相关的参数------ smtpserver = "smtp.163.com" # 发件服务器 port = 0 # 端口 sender = "yoyo@163.com" # 账号 psw = "*********" # 密码 # receiver = ["xxxx@qq.com"] # 单个接收人也可以是list receiver = ["xxxx@qq.com", "yoyo@qq.com"] # 多个收件人list对象 # ----------2.编辑邮件的内容------ # 读文件 file_path = "result.html" with open(file_path, "rb") as fp: mail_body = fp.read() msg = MIMEMultipart() msg["from"] = sender # 发件人 msg["to"] = ";".join(receiver) # 多个收件人list转str msg["subject"] = "这个我的主题999" # 主题 # 正文 body = MIMEText(mail_body, "html", "utf-8") msg.attach(body) # 附件 att = MIMEText(mail_body, "base64", "utf-8") att["Content-Type"] = "application/octet-stream" att["Content-Disposition"] = 'attachment; filename="test_report.html"' msg.attach(att) # ----------3.发送邮件------ try: smtp = smtplib.SMTP() smtp.connect(smtpserver) # 连服务器 smtp.login(sender, psw) except: smtp = smtplib.SMTP_SSL(smtpserver, port) smtp.login(sender, psw) # 登录 smtp.sendmail(sender, receiver, msg.as_string()) # 发送 smtp.quit() # 关闭 六:邮件收不到的几种原因: 1.Subject和正文内容不要用hello、hehe、test等单词 2.from(发件人)和to(收件人)不要为空, (要不然会被认为是垃圾邮件) 3.找不到的话,先看下垃圾信箱,是不是跑到垃圾箱了 4.如果前几次可以收到,后来收不到了,需改下subject内容 (因为每次都是一个subject,系统也会拒收的,把subject内容设置为动态的是最好的) 5.部分邮箱是ssl加密了的,所以无法发送,如:qq邮箱 (用授权码去登录) 6.要是按照上面的步骤来报错了,说明代码抄错了,多检查几次。 (以上代码均在python2和python3上都测试通过了) 前言 当测试用例写完后,有些模块有改动时候,会影响到部分用例的执行,这个时候我们希望暂时跳过这些用例。 或者前面某个功能运行失败了,后面的几个用例是依赖于这个功能的用例,如果第一步就失败了,后面的用例也就没必要去执行了,直接跳过就行,节省用例执行时间。 一、skip装饰器 skip装饰器一共有四个: @unittest.skip(reason) Unconditionally skip the decorated test. reason should describe why the test is being skipped. 翻译:无条件跳过用例,reason是说明原因。 @unittest.skipIf(condition, reason) Skip the decorated test if condition is true. 翻译:condition为true的时候跳过。 @unittest.skipUnless(condition, reason) Skip the decorated test unless condition is true. 翻译:condition为False的时候跳过。 @unittest.expectedFailure Mark the test as an expected failure. If the test fails when run, the test is not counted as a failure. 翻译:断言的时候跳过。 二、skip案例 运行结果: 测试1 测试4 .ssx ---------------------------------------------------------------------- Ran 4 tests in 0.003s OK (skipped=2, expected failures=1) 三、跳过整个测试类 四、参考代码: # coding:utf-8 import unittest class Test(unittest.TestCase): @unittest.skip(u"无条件跳过此用例") def test_1(self): print "测试1" @unittest.skipIf(True, u"为True的时候跳过") def test_2(self): print "测试2" @unittest.skipUnless(False, u"为False的时候跳过") def test_3(self): print "测试3" @unittest.expectedFailure def test_4(self): print "测试4" self.assertEqual(2, 4, msg=u"判断相等") if __name__ == "__main__": unittest.main()

泰国VS中国直播_泰国VS中国直播免费高清在线观看_泰国VS中国直播在线无插件

泰国VS中国直播_泰国VS中国直播高清免费观看_泰国VS中国直播在线观看免费无插件

泰国VS中国直播_泰国VS中国免费高清在线直播_泰国VS中国免费直播视频直播

泰国VS中国直播_泰国VS中国直播免费观看_泰国VS中国直播无插件观看

泰国VS中国直播_泰国VS中国直播观看免费_泰国VS中国

中国VS泰国直播_中国VS泰国比赛高清免费在线直播_中国VS泰国无插件在线观看视频

中国VS泰国直播_中国VS泰国直播免费高清在线观看_中国VS泰国直播在线无插件

中国VS泰国直播_中国VS泰国直播高清免费观看_中国VS泰国直播在线观看免费无插件

中国VS泰国直播_中国VS泰国免费高清在线直播_中国VS泰国免费直播视频直播

中国VS泰国直播_中国VS泰国直播免费观看_中国VS泰国直播无插件观看

05月20日 英超第37轮 切尔西vs热刺 全场录像回放
2026年06月02日
05月20日 英超第37轮 伯恩茅斯vs曼城 全场录像回放
2026年06月02日
05月19日 英超第37轮 阿森纳vs伯恩利 全场录像回放
2026年06月02日
05月25日 英超第38轮 水晶宫vs阿森纳 全场录像回放
2026年06月02日
05月25日 英超第38轮 桑德兰vs切尔西 全场录像回放
2026年06月02日
05月25日 英超第38轮 利物浦vs布伦特福德 全场录像回放
2026年06月02日
05月25日 英超第38轮 布莱顿vs曼联 全场录像回放
2026年06月02日
05月25日 英超第38轮 热刺vs埃弗顿 全场录像回放
2026年06月02日
05月25日 英超第38轮 曼城vs阿斯顿维拉 全场录像回放
2026年06月02日
05月18日 英超第37轮 纽卡斯尔联vsv西汉姆联 全场录像回放
2026年06月02日
05月17日 英超第37轮 狼队vs富勒姆 全场录像回放
2026年06月02日
05月17日 英超第37轮 利兹联vs布莱顿 全场录像回放
2026年06月02日
05月17日 英超第37轮 埃弗顿vs桑德兰 全场录像回放
2026年06月02日
05月17日 英超第37轮 布伦特福德vs水晶宫 全场录像回放
2026年06月02日
05月17日 英超第37轮 曼联vs诺丁汉森林 全场录像回放
2026年06月02日
05月16日 德甲第34轮 柏林联合vs奥格斯堡 全场录像回放
2026年06月02日
05月16日 德甲第34轮 门兴vs霍芬海姆 全场录像回放
2026年06月02日
05月16日 德甲第34轮 海登海姆vs美因茨 全场录像回放
2026年06月02日
05月16日 德甲第34轮 弗赖堡vs莱比锡 全场录像回放
2026年06月02日
05月16日 德甲第34轮 圣保利vs沃尔夫斯堡 全场录像回放
2026年06月02日
05月16日 德甲第34轮 勒沃库森vs汉堡 全场录像回放
2026年06月02日
05月16日 德甲第34轮 法兰克福vs斯图加特 全场录像回放
2026年06月02日
05月17日 德甲第34轮 拜仁慕尼黑vs科隆 全场录像回放
2026年06月02日
05月16日 德甲第34轮 不莱梅vs多特蒙德 全场录像回放
2026年06月02日
05月18日 法甲第34轮 斯特拉斯堡vs摩纳哥 全场录像回放
2026年06月02日
05月18日 法甲第34轮 洛里昂vs勒阿弗尔 全场录像回放
2026年06月02日
05月18日 法甲第34轮 马赛vs雷恩 全场录像回放
2026年06月02日
05月18日 法甲第34轮 巴黎FCvs巴黎圣日耳曼 全场录像回放
2026年06月02日
05月18日 法甲第34轮 里尔vs欧塞尔 全场录像回放
2026年06月02日
05月18日 法甲第34轮 尼斯vs梅斯 全场录像回放
2026年06月02日
05月18日 法甲第34轮 布雷斯特vs昂热 全场录像回放
2026年06月02日
05月24日 西甲第38轮 皇家马德里vs毕尔巴鄂竞技 全场录像回放
2026年06月02日
05月18日 西甲第37轮 巴塞罗那vs皇家贝蒂斯 全场录像回放
2026年06月02日
05月18日 西甲第37轮 塞维利亚vs皇家马德里 全场录像回放
2026年06月02日
05月18日 西甲第37轮 皇家社会vs瓦伦西亚 全场录像回放
2026年06月02日
05月18日 西甲第37轮 皇家奥维耶多vs阿拉维斯 全场录像回放
2026年06月02日
05月18日 西甲第37轮 巴列卡诺vs比利亚雷亚尔 全场录像回放
2026年06月02日
05月18日 西甲第37轮 奥萨苏纳vs西班牙人 全场录像回放
2026年06月02日
05月18日 西甲第37轮 莱万特vs马略卡 全场录像回放
2026年06月02日
05月24日 西甲第38轮 瓦伦西亚vs巴塞罗那 全场录像回放
2026年06月02日
05月18日 西甲第37轮 埃尔切vs赫塔费 全场录像回放
2026年06月02日
05月18日 西甲第37轮 马德里竞技vs赫罗纳 全场录像回放
2026年06月02日
05月18日 西甲第37轮 毕尔巴鄂竞技vs塞尔塔 全场录像回放
2026年06月02日
05月15日 西甲第36轮 皇家马德里vs皇家奥维耶多 全场录像回放
2026年06月02日
05月15日 西甲第36轮 瓦伦西亚vs巴列卡诺 全场录像回放
2026年06月02日
05月15日 西甲第36轮 赫罗纳vs皇家社会 全场录像回放
2026年06月02日
05月24日 意甲第38轮 博洛尼亚vs国际米兰 全场录像回放
2026年06月02日
05月18日 意甲第37轮 萨索洛vs莱切 全场录像回放
2026年06月02日
05月18日 意甲第37轮 乌迪内斯vs克雷莫内塞 全场录像回放
2026年06月02日
05月18日 意甲第37轮 卡利亚里vs都灵 全场录像回放
2026年06月02日
05月25日 意甲第38轮 AC米兰vs卡利亚里 全场录像回放
2026年06月02日
05月25日 意甲第38轮 维罗纳vs罗马 全场录像回放
2026年06月02日
05月25日 意甲第38轮 都灵vs尤文图斯 全场录像回放
2026年06月02日
05月18日 意甲第37轮 亚特兰大vs博洛尼亚 全场录像回放
2026年06月02日
05月17日 意甲第37轮 国际米兰vs维罗纳 全场录像回放
2026年06月02日
05月17日 意甲第37轮 罗马vs拉齐奥 全场录像回放
2026年06月02日
05月17日 意甲第37轮 比萨vs那不勒斯 全场录像回放
2026年06月02日
05月17日 意甲第37轮 尤文图斯vs佛罗伦萨 全场录像回放
2026年06月02日
05月17日 意甲第37轮 热那亚vsAC米兰 全场录像回放
2026年06月02日
04月24日 NBA季后赛西部首轮G3 掘金vs森林狼 全场录像回放
2026年06月02日
04月24日 NBA季后赛东部首轮G3 骑士vs猛龙 全场录像回放
2026年06月02日
04月23日 NBA季后赛西部首轮G2 太阳vs雷霆 全场录像回放
2026年06月02日
04月24日 NBA季后赛东部首轮G3 尼克斯vs老鹰 全场录像回放
2026年06月02日
04月23日 NBA季后赛东部首轮G2 魔术vs活塞 全场录像回放
2026年06月02日
04月22日 NBA季后赛西部首轮G2 火箭vs湖人 全场录像回放
2026年06月02日
04月22日 NBA季后赛东部首轮G2 76人vs凯尔特人 全场录像回放
2026年06月02日
04月21日 NBA季后赛西部首轮G2 森林狼vs掘金 全场录像回放
2026年06月02日
04月21日 NBA季后赛东部首轮G2 老鹰vs尼克斯 全场录像回放
2026年06月02日
04月21日 NBA季后赛东部首轮G2 猛龙vs骑士 全场录像回放
2026年06月02日
04月20日 NBA季后赛西部首轮G1 开拓者vs马刺 全场录像回放
2026年06月02日
04月20日 NBA季后赛东部首轮G1 魔术vs活塞 全场录像回放
2026年06月02日
04月20日 NBA季后赛西部首轮G1 太阳vs雷霆 全场录像回放
2026年06月02日
04月20日 NBA季后赛东部首轮G1 76人vs凯尔特人 全场录像回放
2026年06月02日
04月19日 NBA季后赛西部首轮G1 火箭vs湖人 全场录像回放
2026年06月02日