pytest是python的一种单元测试框架,同自带的unit test测试框架类似,但pytest更简洁高效。
单元测试:
pip install -U pytest
import pytest
def test_a():
print("test_a")
return 1 + 0
def test_b():
print("test_b")
return 1 / 0
if __name__ == '__main__':
pytest.main()
默认情况下:在main中直接使用pytest的main()方法,会把文件中所有test_*开头的方法执行一遍。
# pytest或加参数都可
pytest -s
单量执行测试文件
import pytest
def test_1():
print("test_1+++")
return 1 + 0
def test_2():
print("test_2--------")
return 1 / 0
if __name__ == '__main__':
# 只运行 test_py2.py文件中的测试方法
pytest.main(["-s", "test_py2.py"])
测试自动触发规则:
在测试目录中或当前的目录中寻找
名称为 test_*.py或*_test.py的文件
Test开头的类,且没有初始化__init__方法
以上目录或类中,test开头的函数或方法
会执行uinit test的测试用例类
运行pytest时,自动读取所在目录中的配置文件pytest.ini。在测试文件所在目录下创建一个pytest.ini
内容如下:注意:以下内容请将 中文全部删掉,否则可能出问题,这里只是为了解释配置的。
[pytest]
; ini文件中的英文分号,都是注释
addopts = -s ;选项参数
testpaths = ./ ;测试模块所在目录
python_files = test_*.py *test.py ;测试文件名称
python_classes = Test_* ;测试类名称规则
python_functions = test_* ;测试函数或者方法命名规则
假如我把python_functions修改为demo_*,那么只有以demo_函数名命名的函数才会被执行。
import pytest
def test_1():
print("test_1+++")
assert 20 == 20
def test_2():
print("test_2--------")
assert "a" in "hello"
if __name__ == '__main__':
pytest.main(["-s", "test_py2.py"])
@pytest.mark.skip("跳过")
def test_2():
print("test_2--------")
return 1 / 0
@pytest.mark.xfail(raises=ZeroDivisionError)
def test_3():
print("test_3--------")
return 1 / 0
比如写了一个函数需要模拟一些参数进行调用,那么可以使用:
parametrize装饰器:
import pytest
@pytest.mark.parametrize(["a", "b"], [(1, 2), (2, 2), (50, 51)])
def test_1(a, b):
print("test_1+++++++")
assert a + b > 100
if __name__ == '__main__':
pytest.main(["-s", "test_py3.py"])
在测试之前和之后执行,用于固定测试环境,及清理回收测试资源。
setup_...和teardown_...
模块的夹具:setup_module()和teardown_module(),在python文件加载前和文件内容结束后执行
import pytest
def setup_module(args):
print("setup_module", args)
def teardown_module(args):
print("teardown_module", args)
def test_fun_a():
print("------------", "test_fun_a")
def test_fun_b():
print("------------", "test_fun_b")
class TestOne:
def test_1(self):
print("------", "test_1")
def test_2(self):
print("------", "test_2")
if __name__ == '__main__':
pytest.main(["-s", "test_py4.py"])
###################################################结果########################
test_py4.py setup_module <module 'test_py4' from 'D:\\environment\\python-workspace\\androidTest\\pytest\\test_py4.py'>
------------ test_fun_a
.------------ test_fun_b
.------ test_1
.------ test_2
.teardown_module <module 'test_py4' from 'D:\\environment\\python-workspace\\androidTest\\pytest\\test_py4.py'>
函数的夹具:setup_function()和teardown_function(),py中函数执行前和执行后执行(注意是函数,不是类中的方法)
import pytest
def setup_function(args):
print("setup_function", args)
def teardown_function(args):
print("teardown_function", args)
def test_fun_a():
print("------------", "test_fun_a")
def test_fun_b():
print("------------", "test_fun_b")
class TestOne:
def test_1(self):
print("------", "test_1")
def test_2(self):
print("------", "test_2")
if __name__ == '__main__':
pytest.main(["-s", "test_py4.py"])
###################################################结果########################
test_py4.py setup_function <function test_fun_a at 0x000001ED1D8C31F8>
------------ test_fun_a
.teardown_function <function test_fun_a at 0x000001ED1D8C31F8>
setup_function <function test_fun_b at 0x000001ED1D8C3288>
------------ test_fun_b
.teardown_function <function test_fun_b at 0x000001ED1D8C3288>
------ test_1
.------ test_2
类的夹具:setup_class()和teardown_class(),类被加载前和销毁后执行
class TestOne:
def setup_class(self):
print("------", "setup___test_1")
def teardown_class(self):
print("------", "teardown___test_1")
def test_1(self):
print("------", "test_1")
def test_2(self):
print("------", "test_2")
if __name__ == '__main__':
pytest.main(["-s", "test_py4.py"])
###################################################结果########################
------ setup___test_1
------ test_1
.------ test_2
.------ teardown___test_1
方法的夹具:setup_method()和teardown_method(),类被加载前和销毁后执行
class TestOne:
def setup_class(self):
print("------", "setup___test_1")
def teardown_class(self):
print("------", "teardown___test_1")
def setup_method(self, args):
print("------", "setup_methods___test_1", args)
def teardown_method(self, args):
print("------", "teardown_methods___test_1", args)
def test_1(self):
print("------", "test_1")
def test_2(self):
print("------", "test_2")
if __name__ == '__main__':
pytest.main(["-s", "test_py4.py"])
###################################################结果########################
setup___test_1
------ setup_methods___test_1 <bound method TestOne.test_1 of <test_py4.TestOne object at 0x000001D972FB28C8>>
------ test_1
.------ teardown_methods___test_1 <bound method TestOne.test_1 of <test_py4.TestOne object at 0x000001D972FB28C8>>
------ setup_methods___test_1 <bound method TestOne.test_2 of <test_py4.TestOne object at 0x000001D972FB2988>>
------ test_2
.------ teardown_methods___test_1 <bound method TestOne.test_2 of <test_py4.TestOne object at 0x000001D972FB2988>>
------ teardown___test_1
import pytest
# 设置夹具
@pytest.fixture()
def before():
print("before")
# 使用夹具
@pytest.mark.usefixtures("before")
def test_1():
print("test_1执行")
# 设置夹具 有返回值
@pytest.fixture()
def login():
print("login")
return "user"
# 使用夹具 入参
def test_2(login):
print("test_2执行")
print(login)
@pytest.fixture(params=[1, 2, 3])
def init_data(request):
# params中有三个元素,那么此方法将执行三遍
print("参数:", request.param)
return request.param
def test_data(init_data):
assert init_data > 2
if __name__ == '__main__':
pytest.main(["-s", "test_py5.py"])
###################################################结果################################
test_py5.py 参数: 1
F参数: 2
F参数: 3
.before
test_1执行
.login
test_2执行
user
================================== FAILURES ===================================
________________________________ test_data[1] _________________________________
init_data = 1
def test_data(init_data):
> assert init_data > 2
E assert 1 > 2
test_py5.py:24: AssertionError
________________________________ test_data[2] _________________________________
init_data = 2
def test_data(init_data):
> assert init_data > 2
E assert 2 > 2
test_py5.py:24: AssertionError
=========================== short test summary info ===========================
FAILED test_py5.py::test_data[1] - assert 1 > 2
FAILED test_py5.py::test_data[2] - assert 2 > 2
========================= 2 failed, 3 passed in 0.03s =========================
安装插件
pip install pytest-html
使用
命令行方式
pytest --html=存储路径/report.html
配置文件方式
[pytest]
addopts = -s --html=./report.html
安装插件
pip install pytest-ordering
使用
添加装饰器@pytest.mark.run(order=x)到测试函数或者方法上。
优先级:
import pytest
@pytest.mark.run(order=0)
def test_1():
print("1")
@pytest.mark.run(order=-3)
def test_2():
print("2")
@pytest.mark.run(order=3)
def test_3():
print("3")
@pytest.mark.run(order=2)
def test_4():
print("4")
@pytest.mark.run(order=1)
def test_5():
print("5")
if __name__ == '__main__':
pytest.main(["-s", "test_py6.py"])
###################################################结果###############################
test_py6.py 1
.5
.4
.3
.2
安装插件
pip install pytest-rerunfailures
使用
配置,--reruns 5 重试5次
[pytest]
addopts = -s --reruns 5
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test
我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您
我有:When/^(?:|I)follow"([^"]*)"(?:within"([^"]*)")?$/do|link,selector|with_scope(selector)doclick_link(link)endend我打电话的地方:Background:GivenIamanexistingadminuserWhenIfollow"CLIENTS"我的HTML是这样的:CLIENTS我一直收到这个错误:.F-.F--U-----U(::)failedsteps(::)nolinkwithtitle,idortext'CLIENTS'found(Capybara::Element