背景

    在测试后端的服务器程序时,经常会遇到测试 A 程序的接口时,A 程序要通过 http 协议访问 B 程序。B 程序返回的数据不一样时,A 程序执行的代码逻辑不一样。而且有些场景,B 程序正常情况下是不容易出现的。比如 http 响应超时,响应非法的数据。所以需要一种方法,可以方便的模拟 B 程序的各种不同的响应。解决依赖服务的方法很多。比如用 nginx 搭建一个静态的 http 服务器。还有其它类似的工具。它门的使用方式大多数都是启动一个进程,然后指定一个模拟响应数据的配置文件

如果只是手动测试。用 nginx 之类的工具来模拟是没问题的。如果是用与自动化测试会有以下缺点:

我期望在自动化测试中模拟服务效果如下:

为了达到这个目的,写了这个 auto-mock-server。已上传到 pypi

auto-mock-server 有如下功能:

1.设置 http 响应的状态码

2. 设置 http 响应的响应头

3.设置 http 响应的 body

4.设置 http 响应的延时时间,用于测试依赖服务响应超时的场景

5.可以在一个模拟服务配置多个不同路径的接口,即可以实现根据不同的请求路径响应不同的数据

6.可以获取模拟服务被请求的请求数据,包括请求方法,请求 url,请求头,请求 body,请求总次数

7.可以把模拟服务部署到远程的机器运行

8.支持关闭模拟服务

auto-mock-server 的使用说明

运行环境:需要安装 python3,在 windox 或者 linux 系统运行

先安装 auto-mock-server,直接 pip install auto-mock-server -i https://pypi.douban.com/simple

以下是 1~4 功能点的使用示例

from mock import MockServerClass

import requests


def test_basic_functions_of_the_mock_server():
   #mock_server_port 是模拟服务的监听端口
   mockserver = MockServerClass(mock_server_port=82)


   mockserver.add_route(route_path="/login", response_status='200', response_headers='{"Test_key":"Test_value"}',
                        response_date='{"Whether login succeeded":true}',processing_time="2")
   """
  add_route 方法的参数说明

   :param route_path:       字符串类型, 模拟接口的请求路径, 如果值为mock_other_path,请求url和其它接口不匹配时,会用此接口响应                 
   :param response_status:  字符串类型,指定响应状态码               
   :param response_headers: 字符串类型,字符串里的内容是一个字典 比如 '{"test_key":"test_value"}',指定模拟服务器的响应头
   :param response_date:    字符串类型,模拟接口的http body响应数据         
   :param processing_time:  字符串类型,指定模拟服务特意 sleep固定的时间,再返回数据,默认值是0,单位是秒
   :return:
   """

   #调用start_mock_server()方法后,才会真正的启动模拟服务
   mockserver.start_mock_server()

   response = requests.get("http://127.0.0.1:82/login")
   #断言模拟模拟服务响应的状态码是add_route函数设置的值
   assert response.status_code == 200

   #断言模拟服务的响应头有add_route设置的值
   assert response.headers["Test_key"] == "Test_value"

   #断言模拟服务的响应body是add_route设置的值
   assert response.text == '{"Whether login succeeded":true}'

   #断言模拟服务器会等待processing_time秒的时间,再返回数据
   assert response.elapsed.total_seconds() >= 2


第 5 个功能点的使用示例

可以在一个模拟服务配置多个不同路径的接口,即可以实现根据不同的请求路径响应不同的数据

from mock import MockServerClass
import requests
def test_add_multiple_routes():
    mockserver = MockServerClass(mock_server_port=82)
    mockserver.add_route(route_path="/login", response_status='200', response_headers='{"test_key":"test_value"}',
                         response_date='{"Whether login succeeded":true}')

    mockserver.add_route(route_path="/login2", response_status='200', response_headers='{"test_key":"test_value"}',
                         response_date='{"Whether login succeeded":false}')
    mockserver.add_route(route_path="/mock_other_path", response_status='200',
                         response_date='other_path_content')
    mockserver.start_mock_server()

    # 这样当在本机请求 http://127.0.0.1:82/login  的时候,模拟服务就会响应 {"Whether login succeeded":true}
    response = requests.get("http://127.0.0.1:82/login")
    assert response.text == '{"Whether login succeeded":true}'

    # 当在本机请求 http://127.0.0.1:82/login2  的时候,模拟服务就会响应 {"Whether login succeeded":false}
    response = requests.get("http://127.0.0.1:82/login2")
    assert response.text == '{"Whether login succeeded":false}'

    #当请求的路径精确匹配,没匹配到路径时,会匹配/mock_other_path
    response = requests.get("http://127.0.0.1:82/test_other_path")
    assert response.text == 'other_path_content'

第 6 个功能点的使用示例。查询模拟服务接口被请求时的数据

在自动化测试中,不仅要判断被测程序处理依赖服务的响应数据是否正确,还要判断被测程序请求依赖服务时发送的请求数据是否正确。这正是这个功能点的使用场景

from mock import MockServerClass
import requests


def test_query_mock_server_request_data():
    mockserver = MockServerClass(mock_server_port=82)
    mockserver.add_route(route_path="/login", response_status='200', response_headers='{"test_key":"test_value"}',
                         response_date='{"Whether login succeeded":true}')
    mockserver.start_mock_server()
    response = requests.post("http://127.0.0.1:82/login", data="test1",headers={"Aa":"bb"})

    #mock_server_request_data_len是MockServerClass类的属性,记录的是被请求的总次数
   #断言模拟服务一共收到一个请求
    assert mockserver.mock_server_request_data_len == 1

    #get_mock_server_request_data_list()返回的是一个列表包含模拟服务收到的所有请求数据,元素是MockServerRequestData类对象
    mockserver_received_first_request = mockserver.get_mock_server_request_data_list()[0]
    """
    MockServerRequestData类有四个属性解释如下 

    request_url是请求的uri,字符串类型
    request_method是请求的http方法,字符串类型
    request_body是请求的body,字符串类型
    request_headers是http请求头,字典类型
    """

    #断言模拟服务收到的第一个请求的请求方法是post请求
    assert mockserver_received_first_request.request_method == "POST"

    #断言模拟服务收到的第一个请求的url是/login
    assert mockserver_received_first_request.request_url == "http://127.0.0.1:82/login"

    #断言模拟服务收到的第一个请求的请求头正确
    print(mockserver_received_first_request.request_headers)
    assert mockserver_received_first_request.request_headers["Aa"] == "bb"

    #断言模拟服务收到的请求body正确
    mock_server_request_data_list = mockserver.get_mock_server_request_data_list()
    assert mockserver_received_first_request.request_body == "test1"


第 7 个功能点,在远程服务器创建一个模拟服务

支持在远程服务器创建模拟服务,这在自动化测试分布式系统很有用。因为这时通常要模拟多个后端服务。比如测试一致性 hash 算法,在一台机器启动模拟服务就不够了,需要在多台机器启动模拟服务。在远程服务器启动模拟服务需要在远程服务器提前安装好 flask。需要有远程服务的 ssh 登录信息。以下是使用示例

from mock import MockServerClass
import requests

def test_deploy_to_remote():
    mock_server_machine = {"ip": "192.168.3.67", "port": "22", "username": "root", "password": "jiexijiexi@@"}
    # 创建模拟服务的对象,这里的 mock_server_machine 是指定启动模拟服务程序的机器登录信息,mock_server_port 是指定模拟服务的监听端口
    mockserver = MockServerClass(remote_mock_server_machine=mock_server_machine, mock_server_port=82)
    # 这里添加一个模拟接口,接口的路径是/login,模拟服务响应的状态码是 200
    mockserver.add_route(route_path="/login", response_status='200', response_headers='{"test_key":"test_value"}',
                         response_date='{"Whether login succeeded":true}')
    # 调用 start_mock_server() 方法后,才会真正的启动模拟服务,这样当用户请求 http://192.168.3.67:82/login  的时候,模拟服务就会响应 {"Whether login succeeded":true}
    mockserver.start_mock_server()

    response = requests.get("http://192.168.3.67:82/login")
    assert response.text == '{"Whether login succeeded":true}'

关闭创建的模拟服务

测试运行结束后,如果不调用 mockserver.stop_mock_server(),模拟服务不会自动关闭。可以通过 mockserver.stop_mock_server() 关闭模拟服务,

from mock import MockServerClass
import requests
mockserver = MockServerClass(mock_server_port=82)
mockserver.stop_mock_server()



↙↙↙阅读原文可查看相关链接,并与作者交流