问题背景

因为测试用例的多样性,我们不能硬编码,比如登录的fixture,不能只写入固定的登录用户名和密码,一个是不方便后期维护,另外一个是不好拓展。
在之前的代码中我们发现,fixture是没办法直接传参的,举个例子:

  1. import pytest
  2. @pytest.fixture
  3. def login(name ,password):
  4. print(f"用户{name},使用密码{password}登录成功")
  5. def test_01(login):
  6. login("admin","123456")
  7. print("正在执行test01")
  8. def test_02(login):
  9. login("user01","666666")
  10. print("正在执行test02")
  11. if __name__ == '__main__':
  12. pytest.main(['fixture传参.py', '-s'])

目的很简单,执行casetest_01的时候使用admin账号,执行casetest_02的时候,使用user01的账号。但是 执行的时候就报错了:

  1. @pytest.fixture
  2. def login(name ,password):
  3. E fixture 'name' not found

好像fixture 默认接受的参数必须是另外一个fixture,但是又不存在另外一个叫name的fixture,所以此时就报异常。
所以,为了解决这一问题,我们要学习新的知识点:@pytest.mark.parametrize

@pytest.mark.parametrize传递参数

之前在讲fixture的参数 params的时候,讲到过request的使用:

  1. import pytest
  2. # 定义的夹具函数,使用装饰器pytest.fixture
  3. @pytest.fixture(params=["胡八万","胡三条","胡七筒"])
  4. def login(request):
  5. temp = request.param
  6. print(f"login:用户{temp}执行登录操作")
  7. return temp
  8. #当fixture 的params参数 会被循环执行
  9. def test_01(login):
  10. print(f"{login}已经登录成功~")
  11. if __name__ == '__main__':
  12. pytest.main(['MyPytest.py', '-s'])

执行结果:

  1. collected 3 items
  2. MyPytest.py login:用户胡八万执行登录操作
  3. 胡八万已经登录成功~
  4. .login:用户胡三条执行登录操作
  5. 胡三条已经登录成功~
  6. .login:用户胡七筒执行登录操作
  7. 胡七筒已经登录成功~
  8. .
  9. ============================== 3 passed in 0.07s ==============================
  10. ***Repl Closed***

request是fixture的传参的固定写法
为了解决上述问题,我们使用@pytest.mark.parametrize配合request可以完美解决:

  1. import pytest
  2. @pytest.fixture
  3. def login(request):
  4. param = request.param
  5. print(f"用户{param['name']},使用密码{param['password']}登录成功")
  6. data01 = [{'name': 'admin', 'password': '123456'}]
  7. @pytest.mark.parametrize("login", data01, indirect=True)
  8. def test_01(login):
  9. print("正在执行test01")
  10. data02 = [{'name': 'user01', 'password': '666666'}]
  11. @pytest.mark.parametrize("login", data02, indirect=True)
  12. def test_02(login):
  13. print("正在执行test02")
  14. if __name__ == '__main__':
  15. pytest.main(['fixture传参.py', '-s'])

执行结果:

  1. collected 2 items
  2. fixture传参.py
  3. 用户admin,使用密码123456登录成功
  4. 正在执行test01
  5. .
  6. 用户user01,使用密码666666登录成功
  7. 正在执行test02
  8. .
  9. ============================== 2 passed in 0.07s ==============================
  10. ***Repl Closed***

参数讲解

indirect = True是将login作为一个函数去执行,而不是一个参数,data则是这个函数的入参。
data本质是一个列表,如果入参是fixture是单个入参,则data可以定义为:

data = [“key1”,”key2”,… …]

那么执行case则遍历执行key1key2

如果fixture是多个参数的入参,则data需要传入字典形式:

data=[{“key1”:”value1”},{“key2”:”value2”}… … ]

同理函数遍历执行{"key1":"value1"},{"key2":"value2"}的场景

使用多个fixture的入参

@pytest.mark.parametrize 叠加使用
  1. import pytest
  2. @pytest.fixture
  3. def openBrowser(request):
  4. """打开不同类型的浏览器"""
  5. browserType = request.param
  6. print(f"正在使用{browserType}浏览器")
  7. @pytest.fixture
  8. def login(request):
  9. param = request.param
  10. print(f"用户{param['name']},使用密码{param['password']}登录成功")
  11. browserTypes = ["IE"]
  12. userInfo = [{'name': 'admin', 'password': '123456'}]
  13. @pytest.mark.parametrize("login", userInfo, indirect=True)
  14. @pytest.mark.parametrize("openBrowser", browserTypes, indirect=True)
  15. def test_01(openBrowser, login):
  16. print("正在执行test01")
  17. if __name__ == '__main__':
  18. pytest.main(['fixture传参.py', '-s'])

运行结果:

  1. collected 1 item
  2. fixture传参.py
  3. 正在使用IE浏览器
  4. 用户admin,使用密码123456登录成功
  5. 正在执行test01
  6. .
  7. ============================== 1 passed in 0.07s ==============================
  8. ***Repl Closed***

@pytest.mark.parametrize 合并使用
  1. import pytest
  2. @pytest.fixture
  3. def openBrowser(request):
  4. """打开不同类型的浏览器"""
  5. browserType = request.param
  6. print(f"正在使用{browserType}浏览器")
  7. @pytest.fixture
  8. def login(request):
  9. param = request.param
  10. print(f"用户{param['name']},使用密码{param['password']}登录成功")
  11. data = [('IE', {'name': 'admin', 'password': '123456'})]
  12. @pytest.mark.parametrize("openBrowser,login", data, indirect=True)
  13. def test_01(openBrowser, login):
  14. print("正在执行test01")
  15. if __name__ == '__main__':
  16. pytest.main(['fixture传参.py', '-s'])

执行结果:

  1. fixture传参.py
  2. 正在使用IE浏览器
  3. 用户admin,使用密码123456登录成功
  4. 正在执行test01
  5. .
  6. ============================== 1 passed in 0.07s ==============================
  7. ***Repl Closed***

如果我们想测试多种场景即可改动data的数据,进行参数化:

  1. import pytest
  2. @pytest.fixture
  3. def openBrowser(request):
  4. """打开不同类型的浏览器"""
  5. browserType = request.param
  6. print(f"正在使用{browserType}浏览器")
  7. @pytest.fixture
  8. def login(request):
  9. param = request.param
  10. print(f"用户{param['name']},使用密码{param['password']}登录成功")
  11. data = [('IE', {'name': 'admin', 'password': '123456'}),('IE', {'name': 'root', 'password': '888888'}),('Chrome', {'name': 'admin', 'password': '123456'})]
  12. @pytest.mark.parametrize("openBrowser,login", data, indirect=True)
  13. def test_01(openBrowser, login):
  14. print("正在执行test01")
  15. if __name__ == '__main__':
  16. pytest.main(['fixture传参.py', '-s'])

执行结果:

  1. fixture传参.py 正在使用IE浏览器
  2. 用户admin,使用密码123456登录成功
  3. 正在执行test01
  4. .正在使用IE浏览器
  5. 用户root,使用密码888888登录成功
  6. 正在执行test01
  7. .正在使用Chrome浏览器
  8. 用户admin,使用密码123456登录成功
  9. 正在执行test01
  10. .
  11. ============================== 3 passed in 0.07s ==============================
  12. ***Repl Closed***