【笔记】接口测试/接口自动化测试面试题目(持续更新...)

面试题目

1、当你使用requests库进行接口测试时,如何处理重定向的请求?

主要影响参数为allow_redirects。
默认情况下allow_redirects=True,即自动处理重定向请求。

import requests

# 发起请求,允许重定向
response = requests.get('http://example.com', allow_redirects=True)

# 处理响应
print(response.status_code)
print(response.url)
print(response.history)  # 历史重定向信息
print(response.text)

如果手动设置allow_redirects=False,则需手动处理重定向请求。
重定向地址从首次请求返回响应的headers中取location字段。

import requests

# 发起请求,但不允许重定向
response = requests.get('http://example.com', allow_redirects=False)

# 检查是否有重定向
if response.status_code == 302:
    # 如果有重定向,获取重定向的地址
    redirect_url = response.headers['Location']
    # 再次发起请求到重定向的地址
    redirected_response = requests.get(redirect_url)
    # 处理重定向后的响应
    print(redirected_response.text)
else:
    # 处理正常的响应
    print(response.text)

2、 在PyTest中,如何使用fixture实现接口登录的前置操作?

核心思路是定义一个fixure,在fixure中完成接口登录操作获取token,从而让后续的测试脚本可以显式或隐式地通过该token完成业务接口调用操作。
以先login再profile的业务流程为例。

base_api定义

import os

import allure
import requests
from utils.log_utils import logger

from utils.path_utils import get_root_path
from utils.token_utils import TokenUtils


class BaseApi:

    def __init__(self, account, password=None):
        self.base_url = 'https://evercraft.co'
        # 先从文件中获取token,如果获取失败则重新登录
        with allure.step('获取token'):
            try:
                self.token = TokenUtils.get_token_from_file(account)
            except:
                self.token = ''
            if self.token == '':
                login_r = self.login({'account': account, 'password': password})
                token = login_r.json()['data']['access_token']
                token_path = os.sep.join([get_root_path(), 'data', 'token', f'token_{account}'])
                with open(token_path, 'w+') as f:
                    f.write(token)

    def send_request(self, **kwargs):
        r = requests.request(**kwargs)
        return r

    def login(self, data):
        url = self.base_url + '/example/v2/user/sign_in'
        kwargs = {
            'method': 'POST',
            'url': url,
            'json': data
        }
        r = self.send_request(**kwargs)
        logger.info(r.text)
        return r


User类定义

from apis.base_api_xyz import BaseApi
from utils.log_utils import logger


class User(BaseApi):

    def get_profile(self):
        url = self.base_url + '/example/v2/user/profile'
        kwargs = {
            'method': 'GET',
            'url': url,
            'headers': {'Authorization': f'Bearer {self.token}'}
        }
        r = self.send_request(**kwargs)
        logger.info(r.text)
        return r

fixture定义

import pytest
from apis.evercraft.user import User

@pytest.fixture(scope='session')
def login():
    yield User('ErasedLau@163.com', '123456')

测试脚本

class TestGetProfile:
    
    # 调用login fixture
    def test_get_profile(self, login):
        user = login
        r = user.get_profile()
        assert r.status_code == 200
        r_json = r.json()
        assert r_json['code'] == 0
        assert r_json['data']['name'] == 'UserName'

3、描述一个你之前设计的接口自动化框架的结构,包括目录结构、关键模块等。

  1. 【接口合集】对应框架目录中的apis目录,主要是对各种业务接口对象的抽象,类似于UI自动化测试中的PO但不完全等同于PO。以“项目”对象为例,我们可以封装一个Project类,然后将add、delete、modify、get方法完善,分别对应到“项目”的增删改查接口。
  2. 【用例目录】对应框架目录中的testcase目录,主要就是归档各个模块的测试用例以及前后置脚本(fixture、setup、teardown)
  3. 【工具目录】对应框架目录中的utils目录,主要包含db、logger、json_schema、webhook等工具
  4. 【配置目录】对应框架目录中的configs目录,主要包含像env、db之类的配置信息(yaml文件)
  5. 【数据目录】对应框架目录中的data目录,主要存储测试用例所需数据(yaml文件)
  6. 【日志目录】对应框架目录中的logs目录,主要存储测试用例执行过程中产生的日志
  7. 【报告目录】对应框架目录中的reports目录,存储测试用例执行完毕后产生的allure测试报告
  8. 【测试入口】使用makefile封装简化执行测试的入口

4、如何在PyTest中实现接口返回结果的断言?

  1. 断言响应状态码
assert r.status_code == 200
  1. 断言响应文本(通常是json文本)
assert r.json()['data']['name'] == 'user name'
  1. 借助json_schema断言json结构体

class JsonSchemaUtils:

    @classmethod
    def validate_json_schema(cls, obj, **kwargs):
        """通过jsonschema校验json"""
        if 'file' in kwargs:
            with open(kwargs['file']) as f:
                schema = json.load(f)
        else:   # 'schema' in kwargs
            schema = kwargs['schema']
        try:
            jsonschema.validate(obj, schema)
            return True
        except Exception as e:
            print(f'jsonschema验证失败,捕获异常:\n{e}')
            return False

5、在接口自动化测试中,如何处理接口之间的依赖关系?

  1. 【内部接口】如果待测试接口依赖于系统内部其他接口,则直接调用该接口返回对应依赖数据进行测试,如调用登录接口获取token用于后续业务接口测试。
  2. 【外部依赖】如果待测试接口依赖于第三方系统接口,通常采用mock的方式模拟返回需要的响应数据

6、请描述如何使用Python的requests库进行文件上传操作。

(待学习整理…)

7、在设计接口自动化框架时,如何确保测试数据的隔离性,避免测试之间的数据干扰?

测试数据之间的数据干扰,我经历过的有2种:

  1. 同一用例的测试数据干扰】对于单条测试用例而言,如果测试数据是固定的,那么用例执行完成后如果没有进行后置清理,就会导致下一次执行测试失败。对于这种情况,每次测试时在测试数据后面加上时间戳,并在测试执行前后进行环境清理可以解决问题。
  2. 不同用例的测试数据干扰】对于多条测试用例而言,如果使用相同的测试数据,也有可能影响到其他用例。对于这种情况,针对不同的用例设置单独的测试数据可以解决问题。

8、解释HTTP状态码 201 和 204 的区别及应用场景。

(待学习整理…)

9、描述如何使用requests库处理HTTPS请求中的SSL证书验证。

(答案可能不够完善)
主要通过requests请求中的verify参数来控制。

  1. 当verify=False时,禁用SSL证书验证,可用于临时规避测试环境无HTTPS的测试情况。
  2. 当verify传入具体证书地址时可开启SSL证书验证。
import requests

# 指定SSL证书的路径
cert_path = '/path/to/certificate.pem'

# 发送HTTPS请求并验证SSL证书
response = requests.get('https://example.com', verify=cert_path)

# 处理响应
print(response.text)

10、在PyTest中如何实现参数化测试,提供一个实例。

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support import expected_conditions


class TestParametrize:

    def setup_method(self):
        service = Service(executable_path=ChromeDriverManager().install())
        self.driver = webdriver.Chrome(service=service)
        self.driver.implicitly_wait(10)
        self.driver.get("https://example.com/sign/in")
        password_login_entry_el = self.driver.find_element(By.XPATH, '//span[text()="密码登录"]')
        WebDriverWait(self.driver, 5).until(expected_conditions.element_to_be_clickable(password_login_entry_el))
        password_login_entry_el.click()
        WebDriverWait(self.driver, 5).until(
            expected_conditions.text_to_be_present_in_element((By.TAG_NAME, 'h2'), '密码登录')
        )

    def teardown_method(self):
        self.driver.quit()

    @pytest.mark.parametrize('account,password,target', [
        ('13145899433', '123456', '选择进入身份'), ('13145899433', '1234567', '密码有误')
    ])
    def test_parametrize(self, account, password, target):
        account_input_el = self.driver.find_element(By.CSS_SELECTOR, 'input[placeholder="请输入手机号码或邮箱"]')
        password_input_el = self.driver.find_element(By.CSS_SELECTOR, 'input[placeholder="请输入密码"]')
        account_input_el.send_keys(account)
        password_input_el.send_keys(password)
        login_button_el = self.driver.find_element(By.XPATH, '//button[text()="登录"]')
        login_button_el.click()
        target_el = self.driver.find_element(By.XPATH, f'//*[contains(text(), "{target}")]')
        assert target_el.is_displayed()

11、描述在接口测试中使用Mock和Stub的优势和应用场景。

(待学习整理…)

12、如何处理大文件的上传和下载,并在接口测试中的应用。

(待学习整理…)

13、描述如何将接口自动化测试集成到CI/CD流程中。

(待学习整理…)

14、请描述HTTP协议的工作流程,包括TCP三次握手和四次挥手。

(待学习整理…)