喝杯咖啡的功夫即可学会契约测试☕️

契约测试的要点

1. 契约测试的核心原理是通过定义接口的契约(Contract),即接口的预期行为,然后在测试中验证实际的接口行为是否符合契约的要求。
2. 契约是具体验证什么?契约可包括接口的输入参数、输出结果、异常情况等方面的规定。通过定义一份"合同",我们可以明确接口应该如何被正确使用,以及它应该返回哪些数据和状态
3. 举例说明:
在实际测试中,我们会编写针对每个接口的测试用例,并使用具体的请求数据调用接口,并获取其返回结果。然后,我们会对返回结果进行断言,验证其与契约定义的预期结果是否一致。如果断言失败,就意味着接口的行为与契约不符,测试将失败。

一个用户注册的接口的例子:
对于用户注册接口,首先契约应规定必须提供用户名和密码作为输入,调用成功后返回一个唯一用户ID。那么在测试中,我们会编写一个测试用例,传入合法的用户名和密码,调用接口并获得返回结果。接着,我们会对返回结果进行断言,验证是否包含预期的用户ID。如果断言失败,就表示接口的实际行为不符合契约,测试将失败。
通过验证接口行为是否符合契约的原理,契约测试能够有效地保证接口的正确性和稳定性。它提供了一种可靠的测试方法,既可以在开发过程中验证接口的正确性,也可以在持续集成流程中作为一项重要的测试环节。帮助我们更有信心地保证接口在不同场景下的行为都是符合预期,提高系统的质量和可靠性。

代码展示

  1. 安装pytest和pytest-contract:使用pip命令安装pytest和pytest-contract两个库:

    pip install pytest
    pip install pytest-contract
    
  2. 编写契约文件:根据接口的定义,编写契约文件,其中包括接口的输入参数和输出结果的类型定义和验证逻辑。契约文件一般采用YAML或JSON格式,例如:

    # 注册用户接口的契约文件
    post /register:
      request:
        name: str
        age: int
      response:
        success: bool
        message: str
    
  3. 编写契约测试代码:根据契约文件中的定义,编写契约测试代码,包括对请求参数和响应结果的类型和值的验证。pytest-contract提供了一个contract()装饰器,可以方便地编写契约测试,例如:

    # 注册用户接口的契约测试代码
    from pytest_contract import contract
    from faker import Faker
    
    fake = Faker()
    
    @contract('register.yml')
    def test_register(requests):
        # 生成测试数据
        name = fake.name()
        age = fake.random_int(min=18, max=60)
    
        # 发送接口请求
        response = requests.post('/register', json={'name': name, 'age': age})
    
        # 验证响应结果
        assert response.status_code == 200
        assert response.json()['success'] == True
    
  4. 运行契约测试:在终端执行pytest命令,可以运行契约测试,例如:

    pytest test_register.py
    
  5. 查看测试报告:pytest-contract会自动生成测试报告,可查看到各个接口的测试结果和失败原因。

丰富基于pytest-contract的契约测试框架

  1. 增加参数化测试:

    import pytest
    
    @pytest.mark.parametrize('name, age', [
        ('Alice', 25),
        ('Bob', 30),
        ('Charlie', 35)
    ])
    def test_register(requests, name, age):
        response = requests.post('/register', json={'name': name, 'age': age})
        assert response.status_code == 200
        assert response.json()['success'] == True
    
  2. 增加高级验证逻辑:

    def validate_date_in_range(date_str, start_date, end_date):
        date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
        return start_date <= date <= end_date
    
    @contract('schedule.yml')
    def test_schedule_event(requests):
        start_date = '2023-01-01'
        end_date = '2023-12-31'
    
        response = requests.get('/schedule')
        for event in response.json():
            assert validate_date_in_range(event['date'], start_date, end_date)
    
  3. 增加数据生成规则:

    from faker.providers import BaseProvider
    
    class CustomProvider(BaseProvider):
        def phone_number(self):
            return '555-1234'  # 自定义生成手机号码的规则
    
    fake.add_provider(CustomProvider)
    
    def test_generate_phone_number():
        number = fake.phone_number()
        assert number == '555-1234'
    
  4. 加入re匹配断言:

    import re
    
    def test_search_result(requests):
        response = requests.get('/search?q=test')
        assert response.status_code == 200
        assert 'test' in response.text
    
        pattern = re.compile(r'\d+ results found')
        assert pattern.match(response.text)
    
  5. 错误处理和报告:pytest-contract会自动捕获契约验证失败的异常,并提供详细的错误信息和报告。可以直接运行pytest命令查看输出结果。

    @pytest.mark.contract
    def test_contract_validation(requests):
       response = requests.get('/user')
       assert response.status_code == 200
       assert 'username' in response.json()
       assert 'email' in response.json()
    

    在执行上述测试用例时,如果契约验证失败,pytest-contract会捕获异常,并提供详细的错误信息和报告,例如指出具体哪个断言失败,方便开发人员定位问题。

  6. 数据库和外部服务的集成,可使用mock库模拟数据库和外部服务的交互,确保接口在不同情况下的正确性:

    import pytest
    from unittest import mock
    
    def get_user_from_db(user_id):
        # 从数据库中获取用户信息的实际逻辑
        pass
    
    @pytest.mark.contract
    def test_get_user(requests):
        user_id = 1
        with mock.patch('app.get_user_from_db', return_value={'id': user_id, 'name': 'Alice'}):
            response = requests.get(f'/users/{user_id}')
            assert response.status_code == 200
            assert response.json() == {'id': user_id, 'name': 'Alice'}
    

    在上述测试用例中,通过使用mock.patch装饰器,我们可以模拟get_user_from_db函数的行为,使其返回我们指定的数据,从而在不依赖实际数据库的情况下进行测试。

  7. 集成到持续集成流程:结合持续集成工具,例如Jenkins、GitLab CI等,在项目的CI配置中添加契约测试的步骤,这里使用Jenkins作为持续集成工具,在项目的CI配置中添加契约测试步骤。Jenkinsfile配置如下:

    pipeline {
        agent any
    
        stages {
            stage('Build') {
                steps {
                    // 构建步骤
                }
            }
            stage('Test') {
                steps {
                    // 运行单元测试、集成测试等其他测试步骤
    
                    // 执行契约测试
                    sh 'pytest --contract'
                }
            }
            stage('Deploy') {
                steps {
                    // 部署步骤
                }
            }
        }
    }
    

    上述Jenkinsfile中的Test阶段添加了执行契约测试的步骤,通过运行pytest命令并指定--contract参数,可以只运行契约测试,并生成相应的报告和统计信息。


以上案例都可以丰富在实际项目中基于pytest-contract的契约测试框架,最终集成到持续CI流程中,以确保接口的正确性和稳定性。请注意,契约测试只能验证接口的输入和输出类型和格式是否符合契约文件的定义,不能完全替代功能测试,因此需要结合其他测试方法来进行API接口的全面测试。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木法星人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值