有人有使用 Python Zeep 和 Mock 对 SOAP API 进行单元测试的示例吗?

Does someone have an example of Unit-Testing a SOAP API with Python Zeep and Mock?

我正在构建一个 Python 应用程序,它使用 Python-zeep 访问第三方 SOAP API。我想使用模拟响应实施一些单元测试,因为我并不总是有实时服务器来 运行 我的测试。

我是单元测试的新手,不太确定从哪里开始。我看过在请求库中使用 mock 的示例,但不确定如何将其转换为 Zeep。

在我的一个模型上,我有一种方法可以从 SOAP API 获取所有 DevicePool。这是相关代码的摘录(我使用辅助方法来提供服务对象,因为我计划在许多其他方法中使用它)。

# Get Zeep Service Object to make AXL API calls
service = get_axl_client(self)

# Get list of all DevicePools present in the cluster
resp = service.listDevicePool(searchCriteria={'name': '%'},
                              returnedTags={'name': '', 'uuid': ''})

我想模拟 resp 对象,但是这是 zeep.objects.ListDevicePoolRes 类型(基于 WSDL 解析的动态类型),我不能只实例化具有静态值的对象。

也许我在这里走错了路,必须更深入一点,实际上模拟 zeep 库的一些内部结构,并在 zeep 解析 XML 之前替换请求响应?

如果有人有类似的例子,将不胜感激。

查看 Python Zeep 源代码后,我发现了一些使用请求模拟库的测试示例,我可以将其用于我的解决方案。这是一个工作示例,以防其他人尝试做类似的事情。

我没有在这个例子中做任何断言,因为我首先想证明我可以让 zeep 正确解析模拟响应的概念。

# -*- coding: utf-8 -*-

from zeep import Client
from zeep.cache import SqliteCache
from zeep.transports import Transport
from zeep.exceptions import Fault
from zeep.plugins import HistoryPlugin
from requests import Session
from requests.auth import HTTPBasicAuth
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
from lxml import etree
import requests_mock

disable_warnings(InsecureRequestWarning)

username = 'administrator'
password = 'password'
host = 'cucm-pub'

wsdl = 'file://C:/path/to/axlsqltoolkit/schema/current/AXLAPI.wsdl'
location = 'https://{host}:8443/axl/'.format(host=host)
binding = "{http://www.cisco.com/AXLAPIService/}AXLAPIBinding"

session = Session()
session.verify = False
session.auth = HTTPBasicAuth(username, password)

transport = Transport(cache=SqliteCache(), session=session, timeout=20)
history = HistoryPlugin()
client = Client(wsdl=wsdl, transport=transport, plugins=[history])
service = client.create_service(binding, location)

def test_listPhone():
    text = """<?xml version="1.0" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <ns:listDevicePoolResponse xmlns:ns="http://www.cisco.com/AXL/API/11.5">
      <return>
        <devicePool uuid="{1B1B9EB6-7803-11D3-BDF0-00108302EAD1}">
          <name>AU_PER_DP</name>
        </devicePool>
        <devicePool uuid="{BEF5605B-1DB0-4157-0176-6C07814C47AE}">
          <name>DE_BLN_DP</name>
        </devicePool>
        <devicePool uuid="{B4C65CAB-86CB-30FB-91BE-6F6E712CACB9}">
          <name>US_NYC_DP</name>
        </devicePool>
      </return>
    </ns:listDevicePoolResponse>
  </soapenv:Body>
</soapenv:Envelope>
    """


    with requests_mock.Mocker() as m:
        m.post(location, text=text)
        resp = service.listDevicePool(searchCriteria={'name': '%'},
                                      returnedTags={'name': '',
                                                    'uuid': ''})

    return resp

test_listPhone()

然后这给了我以下结果(为简洁起见,手动删除了 Zeep 包含的所有带有 "none" 的属性):

{
    'return': {
        'devicePool': [
            {
                'name': 'AU_PER_DP',
                'uuid': '{1B1B9EB6-7803-11D3-BDF0-00108302EAD1}'
            },
            {
                'name': 'DE_BLN_DP',
                'uuid': '{BEF5605B-1DB0-4157-0176-6C07814C47AE}'
            },
            {
                'name': 'US_NYC_DP',
                'uuid': '{B4C65CAB-86CB-30FB-91BE-6F6E712CACB9}'
            }
        ]
    },
    'sequence': None
}