今天,我们来把pytest - fixture 玩出点花样

发布于 更新于
本文目录
  1. 自动化测试框架中的 fixture:

技术文章

自动化测试框架中的 fixture:我们在编写测试用例,都会涉及到用例执行之前的环境准备工作,和用例执行之后的环境清理工作。代码版的测试用例也不例外。在自动化测试框架当中,我们也需要编写:用例执行之前的环境准备工作代码(前置工作代码)用例执...

自动化测试框架中的 fixture:


我们在编写测试用例,都会涉及到用例执行之前的环境准备工作,和用例执行之后的环境清理工作。


代码版的测试用例也不例外。在自动化测试框架当中,我们也需要编写:



  • 用例执行之前的环境准备工作代码(前置工作代码)

  • 用例执行之后的环境清理工作(后置工作代码)



通常,在自动化测试框架当中,都叫做 fixture。


pytest 作为 python 语言的测试框架,它的 fixture 有 2 种实现方式。



  • 一种是 xunit-style,跟 unittest 框架的机制非常相似,即 setup/teardown 系列

  • 一种是它自己的 fixture 机制,以@pytest.fixture 装饰器来申明。




xunit-style


        pytest 的 fixture 实现方式一

1




pytest 的 xunit-style 有三个级别的 fixture:测试模块、测试类、测试函数。




1
测试函数/方法级别 


每一个测试函数都会执行的前置和后置。


测试类内部的测试方法:

前置函数名称:setup_method

后置函数名称:teardown_method


模块下的测试函数:

前置函数名称:setup_function

后置函数名称:teardown_function



2
测试类级别 


一个测试类只执行一次前置和后置。


前置函数名称:setup_class

后置函数名称:teardown_class


注意:用@classmethod装饰



3
测试模块级别 


一个测试模块只执行一次前置和后置。

前置函数名称:setup_module

后置函数名称:teardown_module



fromselenium import webdriver
from time import sleep
from random import randint

def setup_module():   
      print("====  模块级的 setup 操作  ====")

def teardown_module():    
      print("====  模块级的 teardown 操作  ====")

def test_random():    
      assert randint(1, 5) == 3

class TestWeb:    

@classmethod    
def  setup_class(cls):       
      print("====  测试类级的 setup 操作  ====")    

@classmethod    
def  teardown_class(cls):        
      print("====  测试类级的 teardown 操作  ====")    

def  setup_method(self):        
      print("====  测试用例级的 setup 操作  ====")      
      self.driver = webdriver.Chrome()    

def  teardown_method(self):        
      print("====  测试用例级的 teardown 操作====")        
      self.driver.quit()     

def test_search(self):        
      self.driver.get("https://www.baidu.com/")       
      self.driver.find_element_by_id("kw").send_keys("柠檬班")         
      self.driver.find_element_by_id("su").click()       
      sleep(1)



pytest 也支持运行 unittest 的测试用例。支持 unittest 以下特性:


  • @unittest.skip

  • setUp/tearDown;

  • setUpClass/tearDownClass;

  • setUpModule/tearDownModule;




fixture 机制


           pytest 的 fixture 实现方式二

2



通过@pytest.fixture 装饰器来定义 fixture。一个函数被@pytest.fixture 装饰,那么这个函数就是 fixture。


使用 fixture 时,分为二个部分:fixture 定义、fixture 调用。

除此之外,还有 fixture 的共享机制,嵌套调用机制。



1

定义 fixture 




1)fixture 通过函数实现。


2)使用@pytest.fixture 进行装饰


import pytest

@pytest.fixture
def  init():    
      pass



3)前置准备工作代码和后置清理工作代码,都写在一个函数里面。


4)通过 yeild 关键字,区分前置代码和后置代码 。yeild 之前的代码为前置代码,yeild 之后的代码为后置代码


在实际应用场景当中,可以只有前置准备工作代码,也可以只有后置清理工作代码。



import  pytest

@pytest.fixture
def init():   
      print("用例执行之前,执行的代码")  # 前置代码   
      yield    
      print("用例执行之后,执行的代码")  # 后置代码 

@pytest.fixture
def init2():    
      print("用例执行之前,执行的代码")  # 只有用例执行之前的前置准备代码
  
@pytest.fixture
def init3():    
      yield    
      print("用例执行之后,执行的代码")  # 只有用例执行之后的后置清理代码



5)fixture 有 4 个作用域:测试会话(session)、测试模块(module)、测试类(class)、测试用例(function)


测试会话:pytest 执行测试用例的整个过程,称为会话。


比如pytest收集到了100条用例并执行完成,这个过程称为测试会话。


设置 fixture 的作用域:通过@pytest.fixture(scope= 作用域)来设置。默认情况下,scope=function


import pytest


# 没有设置scope,默认为测试函数级别。即调用此fixture的测试类/模块/函数下,每个测试函数都会执行一次这个fixture

@pytest.fixture

def init():    

      print("用例执行之前,执行的代码")  # 前置代码    

      yield    

      print("用例执行之后,执行的代码")  # 后置代码



# 设置scope为class。调用此fixture的测试类下,只执行一次这个fixture.

@pytest.fixture(scope="class")

def init2():    

print("用例执行之前,执行的代码")  # 只有用例执行之前的前置准备代码


# 设置scope为session。autouse表示自动使用。

# 那么在pytest收集用例后,开始执行用例之前会自动化执行这个fixture当中的前置代码。

# 当所有用例执行完成之后,自动化执行这个fixture的后置代码。

@pytest.fixture(scope="session",autouse=True)

def init3():    

      yield    

      print("用例执行之后,执行的代码")  # 只有用例执行之后的后置清理代


6)fixture 的返回值设置:yeild 返回值

当测试用例当中,要使用 fixture 里生成的数据时,则需要 fixture 返回数据。

若有数据返回则:yeild 返回值




import pytest

from selenium import webdriver

from time import sleep


# 设置scope为class。调用此fixture的测试类下,只执行一次这个fixture.

@pytest.fixture(scope="class")

def init2():    

       print("==== 测试类下,执行所有用例之前,执行的代码 ====")    

       driver = webdriver.Chrome()    

       yield driver   # 返回driver对象    

       print("==== 测试类下,执行所有用例之后,执行的代码 ====")    

       driver.quit()



2
调用 fixture 


在 fixture 定义好之后,可以明确:



  • fixture 处理了哪些前置准备工作、哪些后置清理工作

  • fixture 作用在哪个范围(是测试函数?还是测试类?还是测试会话?还是测试模块?)


在以上 2 点都定下来了之后,接下来就是,在测试用例当中,根据需要调用不同的 fixture。



调用方法有 2 种:


  1. 在测试用例/测试类上面加上:@pytest.mark.usefixture("fixture 的函数名字")

  2. 将 fixture 函数名,作为测试用例函数的参数。



第2种用法,主要是用参数来接收fixture的返回值,以便在测试用例中使用。



  • 第一种方式案例如下:



  • 第二种方式案例如下:



3
conftest.py 共享机制 


在某些大的业务场景下,很多用例当中,会使用相同的前置准备工作,和后置清理工作。如果在每个测试模块下,都把前置准备工作,和后置清理工作写一遍,在维护上和优化上讲不够好。


pytest 框架提供了一个 fixture 共享的机制 ,可以让不同的用例模块,使用同一个 fixture。这就是 conftest.py 文件。


 conftest.py 共享实现


  • 在项目根目录下,创建一个 conftest.py 文件。

  • 文件名必须是 conftest.py,大小写敏感,不可改名字。

  • conftest.py 当中,可以编写多个 fixture

  • 在测试用例文件当中,不需要引入 conftest.py 文件。直接调用 fixture 的函数名,会自动去 conftest.py 当中查找的。




conftest.py 层级作用域

conftest.py 在项目根目录下,则项目下的所有测试用例,均可使用 conftest.py 中定义的 fixture。即项目根目录下的 conftest.py,作用域是整个项目。


那,如果,conftest.py 当中的 fixture,只想在某个 python 包内可用呢?

conftest.py 实现了层级作用域。


简单来说就是:conftest.py 在哪个目录下,此目录下(包含子目录)的所有用例可使用其中的 fixture。


如下图:


  • 根目录下的 conftest.py 里的 fixture,无论项目下的哪个用例,都可以使用。

  • 子目录 moduleA 下的 conftest.py 里的 fixture,只有 moduleA 下的用例可以使用。

  • 子目录 moduleB 下的 conftest.py 里的 fixture,只有 moduleB 下的用例可以使用。




moduleB 下的用例文件 test_module_b.py 中的用例,即可以使用根目录下的 conftest.py 中的 fixuture,又可以使用自己目录下的 conftest.py 的 fixture:


那么有个问题,如果出现了同名 fixture 怎么办呢?


这里涉及到了,测试用例在执行时,调用 fixture 的顺序。一般来讲,按 就近原则 调用。

测试用例文件中的 fixture > 当前目录中的 fixture > 上级目录中的 fixture > 根目录中的 fixture


4
fixture 嵌套


fixture 不但支持共享 ,还支持嵌套使用。

嵌套使用即:一个 fixture,可以做另外一个 fixture 的参数。

如下图所示:名为 init2 的 fixture,可以作为 init 的参数。

并且,init 当中,将 init2 的返回值,同样返回。


当在用例当中,调用 init 时,init 会自动去调用 init2。

fixture 的执行顺序如下:

  • init2 的前置代码

  • init 的前置代码

  • init 的后置代码

  • init2 的后置代码


下图案例中,init2 为 class 级作用域,init 为 function 级作用域。







文末福利


获取免费pytest相关视频资料



加微信173-5287-1687,备注一下:官网~




继续阅读