6.内置fixture
pytest内置的fixture可以大量简化测试工作。如在处理临时文件时,pytest内置的fixture可以识别命令行参数、在多个测试会话间通信、校验输出流、更改环境变量、审查错误报警等。内置fixture是对pytest核心功能的扩展。
6.1 使用tmpdir和tempdir_factory
内置的tmpdir和tmpdir_factory负责在测试开始运行前创建临时文件目录,并在测试结束后删除。其主要特性如下所示:
- 1.如果测试代码要对文件进行读写操作,可以使用tmpdir或tmpdir_factory来创建文件或目录,单个测试使用tmpdir,多个测试使用tmpdir_factory
- 2.tmpdir的作用范围为函数级别,tmpdir_factory作用范围是会话级别
示例代码如下所示:
import pytest
def test_tmpDir(tmpdir):
tmpfileA=tmpdir.join("testA.txt")
tmpSubDir=tmpdir.mkdir("subDir")
tmpfileB=tmpSubDir.join("testB.txt")
tmpfileA.write("this is pytest tmp file A")
tmpfileB.write("this is pytest tmp file B")
assert tmpfileA.read()=="this is pytest tmp file A"
assert tmpfileB.read()=="this is pytest tmp file B"
tmpdir的作用范围是函数级别,所以只能针对测试函数使用tmpdir创建文件或目录。如果fixture作用范围高于函数级别(类、模块、会话),则需要使用tmpdir_factory。tmpdir与tmpdir_factory类似,但提供的方法有一些不同,如下所示:
import pytest
def test_tmpDir(tmpdir_factory):
baseTmpDir=tmpdir_factory.getbasetemp()
print(f"\nbase temp dir is :{baseTmpDir}")
tmpDir_factory=tmpdir_factory.mktemp("tempDir")
tmpfileA=tmpDir_factory.join("testA.txt")
tmpSubDir=tmpDir_factory.mkdir("subDir")
tmpfileB=tmpSubDir.join("testB.txt")
tmpfileA.write("this is pytest tmp file A")
tmpfileB.write("this is pytest tmp file B")
assert tmpfileA.read()=="this is pytest tmp file A"
assert tmpfileB.read()=="this is pytest tmp file B"
getbasetemp()用于返回该会话使用的根目录,pytest-NUM会随着会话的增加而进行自增,pytest会记录最近几次会话使用的根目录,更早的根目录记录则会被清理掉。另外也可在命令行指定临时目录,如下所示:
pytest --basetemp=dir
运行结果如下所示:
>>> pytest -s -v .\test_01.py
========================= test session starts ==============================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
collected 1 item
test_01.py::test_tmpDir
base temp dir is :C:\Users\Surpass\AppData\Local\Temp\pytest-of-Surpass\pytest-11
PASSED
========================= 1 passed in 0.12s ==================================
6.2 在其他作用范围内使用临时目录
tmpdir_factory的作用范围是会话级别的,tmpdir的作用范围是函数级别的。如果需要模块级别或类级别的作用范围的目录,该如何解决了?针对这种情况,可以利用tmpdir_factory再创建一个fixture。
假设有一个测试模块,其中有很多测试用例需要读取一个JSON文件,则可以在模块本身或conftest.py中创建一个作用范围为模块级别的fixture用于配置该谁的,示例如下所示:
conftest.py
import json
import pytest
@pytest.fixture(scope="module")
def readJson(tmpdir_factory):
jsonData={
"name":"Surpass",
"age":28,
"locate":"shangahi",
"loveCity":{"shanghai":"shanghai",
"wuhai":"hubei",
"shenzheng":"guangdong"
}
}
file=tmpdir_factory.mktemp("jsonTemp").join("tempJSON.json")
with open(file,"w",encoding="utf8") as fo:
json.dump(jsonData,fo,ensure_ascii=False)
# print(f"base dir is {tmpdir_factory.getbasetemp()}")
return file
test_02.py
import json
def test_getData(readJson):
with open(readJson,"r",encoding="utf8") as fo:
data=json.load(fo)
assert data.get("name")=="Surpass"
def test_getLoveCity(readJson):
with open(readJson,"r",encoding="utf8") as fo:
data=json.load(fo)
getCity=data.get("loveCity")
for k,v in getCity.items():
assert len(v)>0
运行结果如下所示:
>>> pytest -v .\test_02.py
============================ test session starts ==============================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
collected 2 items
test_02.py::test_getData PASSED [ 50%]
test_02.py::test_getLoveCity PASSED [100%]
========================== 2 passed in 0.08s ==================================
因为创建的fixture级别为模块级别,因此JSON只会被创建一次。
6.3 使用pytestconfig
内置的pytestconfig可以通过命令行参数、选项、配置文件、插件、运行目录等方式来控制pytest。pytestconfig是request.config的快捷方式,在pytest中称之为”pytest配置对象“
为了理解pytestconfig是如何工作,可以查看如何添加一个自定义的命令行选项,然后在测试用例中读取该选项。另外也可以直接从pytestconfig里读取自定义的命令行选项,为了让pytest能够解析,还需要使用hook函数(hook函数是另一种控制pytest的方法,在插件中频繁使用)。示例如下所示:
pytestconfig\conftest.py
def pytest_addoption(parser):
parser.addoption("--myopt",action="store_true",help="test boolean option")
parser.addoption("--foo",action="store",default="Surpass",help="test stroe")
运行结果如下所示:
>>> pytest --help
usage: pytest [options] [file_or_dir] [file_or_dir] [...]
...
custom options:
--myopt test boolean option
--foo=FOO test stroe
下面来尝试在测试用例中使用这些选项,如下所示:
pytestconfig\test_03.py
import pytest
def test_myOption(pytestconfig):
print(f"--foo {pytestconfig.getoption('foo')}")
print(f"--myopt {pytestconfig.getoption('myopt')}")
运行结果如下所示:
>>> pytest -s -q .\test_03.py
--foo Surpass
--myopt False
.
1 passed in 0.08s
>>> pytest -s -q --myopt .\test_03.py
--foo Surpass
--myopt True
.
1 passed in 0.02s
>>> pytest -s -q --myopt --foo Surpass .\te
st_03.py
--foo Surpass
--myopt True
因为pytestconfig是一个fixture,所以也可以被其他fixture使用。如下所示:
import pytest
@pytest.fixture()
def foo(pytestconfig):
return pytestconfig.option.foo
@pytest.fixture()
def myopt(pytestconfig):
return pytestconfig.option.myopt
def test_fixtureForAddOption(foo,myopt):
print(f"\nfoo -- {foo}")
print(f"\nmyopt -- {myopt}")
运行结果如下所示:
>>> pytest -v -s .\test_option.py
======================== test session starts =============================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04\pytestconfig
collected 1 item
test_option.py::test_fixtureForAddOption
foo -- Surpass
myopt -- False
PASSED
======================= 1 passed in 0.14s ================================
除了使用pytestconfig自定义之外,也可以使用内置的选项和pytest启动时的信息,如目录、参数等。如所示:
def test_pytestconfig(pytestconfig):
print(f"args : {pytestconfig.args}")
print(f"ini file is : {pytestconfig.inifile}")
print(f"root dir is : {pytestconfig.rootdir}")
print(f"invocation dir is :{pytestconfig.invocation_dir}")
print(f"-q, --quiet {pytestconfig.getoption('--quiet')}")
print(f"-l, --showlocals:{pytestconfig.getoption('showlocals')}")
print(f"--tb=style: {pytestconfig.getoption('tbstyle')}")
运行结果如下所示:
>>> pytest -v -s .\test_option.py
========================== test session starts ==========================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04\pytestconfig
collected 1 item
test_option.py::test_pytestconfig args : ['.\\test_option.py']
ini file is : None
root dir is : C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04\pytestconfig
invocation dir is :C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04\pytestconfig
-q, --quiet 1
-l, --showlocals:False
--tb=style: auto
PASSED
==========================1 passed in 0.07s =================================
6.4 使用cache
通常情况下,每个测试用例彼此都是独立的,互不影响。但有时,一个测试用例运行完成后,希望将其结果传递给下一个测试用例,这种情况下,则需要使用pytest内置的cache。
cache的作用是存在一段测试会话信息,在下一段测试会话中使用。使用pytest内置的--last-failed和--failed-first标识可以很好的展示cache功能。示例如下所示:
def test_A():
assert 1==1
def test_B():
assert 1==2
运行结果如下所示:
>>> pytest -v --tb=no .\test_04.py
=========================== test session starts =========================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
collected 2 items
test_04.py::test_A PASSED [ 50%]
test_04.py::test_B FAILED [100%]
======================== 1 failed, 1 passed in 0.08s ========================
上面有一个测试用例运行失败,再次使用--ff或--failed-first,则之前运行失败的测试用例会首先被运行,然后才运行其他的测试用例,如下所示:
>>> pytest -v --tb=no --ff .\test_04.py
===================== test session starts ===========================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
collected 2 items
run-last-failure: rerun previous 1 failure first
test_04.py::test_B FAILED [ 50%]
test_04.py::test_A PASSED [100%]
======================= 1 failed, 1 passed in 0.14s ===================
另外也可以使用--lf或--last-failed仅运行上次运行失败的测试用例,如下所示:
>>> pytest -v --tb=no --lf .\test_04.py
=================== test session starts ===============================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
collected 1 item
run-last-failure: rerun previous 1 failure
test_04.py::test_B FAILED [100%]
========================1 failed in 0.07s =============================
pytest是如何存储并优先调用的呢?我们先来看看以下这个测试用例,如下所示:
import pytest
from pytest import approx
testData=[
#x,y,res
(1,2,3),
(2,4,6),
(3,5,8),
(-1,-2,0)
]
@pytest.mark.parametrize("x,y,expect",testData)
def test_add(x,y,expect):
res=x+y
assert res==approx(expect)
运行结果如下所示:
>>> pytest -v -q .\test_04.py
================== test session starts =============================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
collected 4 items
test_04.py ...F [100%]
================== FAILURES =======================================
___________________ test_add[-1--2-0] _____________________________
x = -1, y = -2, expect = 0
@pytest.mark.parametrize("x,y,expect",testData)
def test_add(x,y,expect):
res=x+y
> assert res==approx(expect)
E assert -3 == 0 ± 1.0e-12
E + where 0 ± 1.0e-12 = approx(0)
test_04.py:16: AssertionError
=================== short test summary info =======================
FAILED test_04.py::test_add[-1--2-0] - assert -3 == 0 ± 1.0e-12
=================== 1 failed, 3 passed in 0.26s ===================
根据报错提示信息,我们一眼就能找到错误,那针对不是那么好定位的问题的测试用例了,这个时候就需要使用--showlocals(简写-l)来调试失败的测试用例。如下所示:
>>> pytest -q --lf -l .\test_04.py
F [100%]
========================= FAILURES =============================
________________________ test_add[-1--2-0] _____________________
x = -1, y = -2, expect = 0
@pytest.mark.parametrize("x,y,expect",testData)
def test_add(x,y,expect):
res=x+y
> assert res==approx(expect)
E assert -3 == 0 ± 1.0e-12
E + where 0 ± 1.0e-12 = approx(0)
expect = 0
res = -3
x = -1
y = -2
test_04.py:16: AssertionError
======================== short test summary info ====================
FAILED test_04.py::test_add[-1--2-0] - assert -3 == 0 ± 1.0e-12
1 failed in 0.17s
通过以上信息,可以很直观看出问题所在位置,为记住上次测试失败的用例,pytest存储了上一个测试会话中测试失败的信息,可以使用--cache-show标识来显示存储的信息。
>>> pytest --cache-show
======================= test session starts ============================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
cachedir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04\.pytest_cache
----------------------------- cache values for '*' ------------------------
cache\lastfailed contains:
{'pytestconfig/test_03.py::test_myOption': True,
'test_04.py::test_B': True,
'test_04.py::test_add[-1--2-0]': True}
cache\nodeids contains:
['test_01.py::test_tmpDir',
'test_02.py::test_getLoveCity',
'test_02.py::test_getData',
'test_04.py::test_A',
'test_04.py::test_B',
'pytestconfig/test_03.py::test_myOption',
'pytestconfig/test_option.py::test_pytestconfig',
'test_04.py::test_add[1-2-3]',
'test_04.py::test_add[2-4-6]',
'test_04.py::test_add[3-5-8]',
'test_04.py::test_add[-1--2-0]']
cache\stepwise contains:
[]
========================no tests ran in 0.03s ==============================
如果需要清空cache,可以在测试会话之前,传入--clear-cache标识即可,cache除了--lf和--ff两个标识之外,还可以使用其接口,如下所示:
cache.get(key,default)
cache.set(key,value)
习惯上,键名以应用名字或插件名字开始,接着是 / ,然后是分隔开的键字符串。键值可以是任何可转化成JSON的东西,因为在cache目录中是用JSON格式存储的。
下面来创建一个fixture,记录测试的耗时,并存储到cache中,如果后面的测试耗时大于之前的2倍,就抛出超时异常。
import datetime
import time
import random
import pytest
@pytest.fixture(autouse=True)
def checkDuration(request,cache):
key="duration/"+request.node.nodeid.replace(":","_")
startTime=datetime.datetime.now()
yield
endTime=datetime.datetime.now()
duration=(endTime-startTime).total_seconds()
lastDuration=cache.get(key,None)
cache.set(key,duration)
if lastDuration is not None:
errorString="test duration over twice last duration"
assert duration <= 2 * lastDuration,errorString
@pytest.mark.parametrize("t",range(5))
def test_duration(t):
time.sleep(random.randint(0,5))
nodeid是一个独特的标识,即便在参数化测试中也能使用。按以下步骤运行测试用例
>>> pytest -q --cache-clear .\test_04.py
..... [100%]
5 passed in 10.14s
>>> pytest -q --tb=line .\test_04.py
.E....E [100%]
========================== ERRORS ========================================
_________________ ERROR at teardown of test_duration[0] __________________
assert 5.006229 <= (2 * 1.003045)
E AssertionError: test duration over twice last duration
_________________ ERROR at teardown of test_duration[4] ___________________
assert 4.149226 <= (2 * 1.005112)
E AssertionError: test duration over twice last duration
================== short test summary info ================================
ERROR test_04.py::test_duration[0] - AssertionError: test duration over twice last duration
ERROR test_04.py::test_duration[4] - AssertionError: test duration over twice last duration
5 passed, 2 errors in 14.50s
>>> pytest -q --cache-show
cachedir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04\.pytest_cache
----------------------- cache values for '*' -----------------------------------------
cache\lastfailed contains:
{'test_04.py::test_duration[0]': True, 'test_04.py::test_duration[4]': True}
cache\nodeids contains:
['test_04.py::test_duration[0]',
'test_04.py::test_duration[1]',
'test_04.py::test_duration[2]',
'test_04.py::test_duration[3]',
'test_04.py::test_duration[4]']
cache\stepwise contains:
[]
duration\test_04.py__test_duration[0] contains:
5.006229
duration\test_04.py__test_duration[1] contains:
0.001998
duration\test_04.py__test_duration[2] contains:
1.006201
duration\test_04.py__test_duration[3] contains:
4.007687
duration\test_04.py__test_duration[4] contains:
4.149226
no tests ran in 0.03s
因为cache数据有前缀,可以直接看见duration数据。
6.5 使用capsys
pytest内置的capsys主要有两个功能
- 允许使用代码读取stdout和stderr
- 可以临时禁止抓取日志输出
1.读取stdout
def greeting(name):
print(f"Hello,{name}")
def test_greeting(capsys):
greeting("Surpass")
out,err=capsys.readouterr()
assert "Hello,Surpass" in out
assert err==""
运行结果如下所示:
>>> pytest -v .\test_05.py
========================= test session starts ============================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
collected 1 item
test_05.py::test_greeting PASSED [100%]
====================== 1 passed in 0.08s ==================================
2.读取stderr
import sys
def greeting(name):
print(f"Hello,{name}",file=sys.stderr)
def test_greeting(capsys):
greeting("Surpass")
out,err=capsys.readouterr()
assert "Hello,Surpass" in err
assert out==""
运行结果如下所示:
>>> pytest -v .\test_05.py
==========================test session starts =============================
platform win32 -- Python 3.7.6, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- d:\program files\python\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Surpass\Documents\PycharmProjects\PytestStudy\Lesson04
collected 1 item
test_05.py::test_greeting PASSED [100%]
============================ 1 passed in 0.11s ===========================
pytest通常会抓取测试用例及被测试代码的输出。而且是在全部测试会话结束后,抓取到的输出才会随着失败的测试显示出来。--s参数可以关闭该功能,在测试仍在运行时就把输出直接发送到stdout,但有时仅需要其中的部分信息,则可以使用capsys.disable(),可以临时让输出绕过默认的输出捕获机制,示例如下所示:
def test_capsysDisable(capsys):
with capsys.disabled():
print("\nalways print this information")
print("normal print,usually captured")
运行结果如下所示:
>>> pytest -q .\test_05.py
always print this information
. [100%]
1 passed in 0.02s
>>> pytest -q -s .\test_05.py
always print this information
normal print,usually captured
.
1 passed in 0.02s
不管有没有捕获到输出,always print this information始终都会显示,是因为其包含在capsys.disabled()的代码块中执行的。其他的打印语句是正常命令,在传入-s参数才会显示。
-s标识是--capture=no的简写,表示关闭输出捕获
6.6 使用monkeypatch
monkey patch可以在运行期间对类或模块进行动态修改。在测试中,monkey patch常用于替换被测试代码的部分运行环境或装饰输入依赖或输出依赖替换成更容易测试的对象或函数。在pytest内置的monkey patch 允许单一环境中使用,并在测试结束后,无论结果是失败或通过,所有修改都会复原。monkeypatch常用的函数如下所示:
setattr(target, name, value=<notset>, raising=True): # 设置属性
delattr(target, name=<notset>, raising=True): # 删除属性
setitem(dic, name, value): # 设置字典中一个元素
delitem(dic, name, raising=True): # 删除字典中一个元素
setenv(name, value, prepend=None): # 设置环境变量
delenv(name, raising=True): # 删除环境变量
syspath_prepend(path) # 将path路径添加到sys.path中
chdir(path) # 改变当前的工作路径
- 1.raising参数用于指示pytest在记录不存在时,是否抛出异常
- 2.setenv()中的prepend可以是一个字符,如果是这样设置,则环境变量的值就是value+prepend+<old value>
为更好理解monkeypatch的实际应用方式,我们先来看看以下示例:
import os
import json
defaulData={
"name":"Surpass",
"age":28,
"locate":"shangahi",
"loveCity":{"shanghai":"shanghai",
"wuhai":"hubei",
"shenzheng":"guangdong"
}
}
def readJSON():
path=os.path.join(os.getcwd(),"surpass.json")
with open(path,"r",encoding="utf8") as fo:
data=json.load(fo)
return data
def writeJSON(data:str):
path = os.path.join(os.getcwd(), "surpass.json")
with open(path,"w",encoding="utf8") as fo:
json.dump(data,fo,ensure_ascii=False,indent=4)
def writeDefaultJSON():
writeJSON(defaulData)
writeDefaultJSON()既没有参数也没有返回值,该如何测试?仔细观察函数,它会在当前目录中保存一个JSON文件,那就可以从侧面来进行测试。通常比较直接的方法,运行代码并检查文件的生成情况。如下所示:
def test_writeDefaultJSON():
writeDefaultJSON()
expectd=defaulData
actual=readJSON()
assert expectd==actual
上面这种方法虽然可以进行测试,但却覆盖了原有文件内容。函数里面所传递的路径为当前目录,那如果将目录换成临时目录了,示例如下所示:
def test_writeDefaultJSONChangeDir(tmpdir,monkeypatch):
tmpDir=tmpdir.mkdir("TestDir")
monkeypatch.chdir(tmpDir)
writeDefaultJSON()
expectd=defaulData
actual=readJSON()
assert expectd==actual
以上这种虽然解决了目录的问题,那如果测试过程,需要修改数据,又该如何,示例如下所示:
def test_writeDefaultJSONChangeDir(tmpdir,monkeypatch):
tmpDir=tmpdir.mkdir("TestDir")
monkeypatch.chdir(tmpDir)
# 保存默认数据
writeDefaultJSON()
copyData=deepcopy(defaulData)
# 增加项
monkeypatch.setitem(defaulData,"hometown","hubei")
monkeypatch.setitem(defaulData,"company",["Surpassme","Surmount"])
addItemData=defaulData
# 再次保存数据
writeDefaultJSON()
# 获取保存的数据
actual=readJSON()
assert addItemData==actual
assert copyData!=actual
因为默认的数据是字典格式的,所以可以使用setitem来进行添加键值对。
6.7 使用recwarn
内置的recwarn可以用来检查待测代码产生的警告信息。在Python中,我们可以添加警告信息,很像断言,但不阻止程序运行。假如在一份代码,想要停止支持一个已经过时的函数,则可以在代码中设置警告信息,示例如下所示:
import warnings
import pytest
def depricateFunc():
warnings.warn("This function is not support after 3.8 version",DeprecationWarning)
def test_depricateFunc(recwarn):
depricateFunc()
assert len(recwarn)==1
warnInfo=recwarn.pop()
assert warnInfo.category==DeprecationWarning
assert str(warnInfo.message) == "This function is not support after 3.8 version"
recwarn的值就是一个警告信息列表,列表中的每个警告信息都有4个属性category、message、filename、lineno。警告信息在测试开始后收集,如果待测的警告信息在最后,则可以在信息收集前使用recwarn.clear()清除不需要的内容。
除recwarn,还可以使用pytest.warns()来检查警告信息。示例如下所示:
import warnings
import pytest
def depricateFunc():
warnings.warn("This function is not support after 3.8 version",DeprecationWarning)
def test_depricateFunc():
with pytest.warns(None) as warnInfo:
depricateFunc()
assert len(warnInfo)==1
w=warnInfo.pop()
assert w.category==DeprecationWarning
assert str(w.message) == "This function is not support after 3.8 version"