最佳实践:自动化Web API测试

3 投票
3 回答
2914 浏览
提问于 2025-04-17 15:02

我写了一个用Python编写的程序,这个程序通过两个不同的API从两个不同的服务(CKAN和MediaWiki)获取数据。特别是,有一个叫做Resource的类,它负责从这些服务请求数据并处理这些数据。

在某个时候,我意识到我的应用程序需要进行测试。而问题是,我在网上和书籍中找到的所有例子都没有涉及到这种情况。

举个例子,在Resource类里面,我有一个方法:

def load_from_ckan(self):
    """
        Get the resource
        specified by self.id
        from config.ckan_api_url 
    """
    data = json.dumps({'id': self.id})
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
    url = config.ckan_api_url + '/action/resource_show'
    r = requests.post(url, timeout=config.ckan_request_timeout, data=data, headers=headers)
    assert r.ok, r
    resource = json.loads(r.content)
    resource = resource["result"]
    for key in resource:
        setattr(self, key, resource[key])

这个load_from_ckan方法是从CKAN API获取资源的数据,并将其分配给对象。这个过程很简单,但是……

我想问的是:像这样的函数应该怎么测试呢?或者我应该测试什么呢?

我考虑过将结果保存到硬盘上,然后在测试中加载这些结果,并与通过load_from_ckan()初始化的对象进行比较。但是CKAN是一个由社区驱动的平台,这样的测试结果会很不稳定。

如果有关于自动化测试哲学的书籍(比如该测试什么、不该测试什么、如何让测试有意义等),请给我一个链接。

3 个回答

0

现在,你可以测试一下从CKAN得到的回应是否被正确解析了。也就是说,你可以从CKAN获取JSON数据,确保它返回的数据包含你关心的那些属性。

1

通常,如果你想测试一些外部服务,比如API,你需要使用一个模拟对象来假装这个外部服务的接口。这个模拟对象必须能够在运行时进行配置,可以通过方法的参数、类的构造函数或者其他方式来实现。还有一种更复杂的选择是,在测试时修改全局变量,比如“import requests; request.post = fake_post”,但这样可能会引发更多问题。

举个例子,你的方法可以接收一个参数,像这样:

def load_from_ckan(self, post=requests.post):
    # ...
    r = post(url, timeout=config.ckan_request_timeout, data=data,
        headers=headers)
    # ...

然后在测试时,你可以写一个自己的post函数,返回你从ckan看到的json结果。例如:

 def mock_post(url, timeout=30, data='', headers=None):
     # ... probably check input arguments
     class DummyResponse:
         pass
     r = DummyResponse()
     r.ok = True
     r.content = json.dumps({'result': {'attr1': 1, 'attr2': 2}})
     return r

在测试中构造结果比直接返回预先保存的结果要灵活得多,因为你可以制造错误情况,或者专注于一些你的代码可能没有预料到但实际上可能存在的特定格式。

总的来说,你可以看到这可能会变得很复杂,所以我建议只有在你遇到重复错误或其他困难时,才开始添加这种测试。这会增加你需要维护的代码量。

2

在进行任何测试时,最关键的问题就是——有什么可能出错的地方?

在你的情况下,似乎有三个风险:

  • 你使用的网络API可能会停止工作。不过你已经通过 assert r.ok 来检查这一点。
  • 你或者其他人将来可能会对代码做出错误的修改(比如,变量名写错了),这会导致代码出问题。
  • API可能会发生变化,导致它不再返回你需要的字段或格式。

对于后面两个风险,你可以写一些相对简单的测试,具体取决于你依赖这个API的哪些数据。例如,如果你期望返回的JSON中有一个叫“temperature”的字段,它是一个浮点数表示的摄氏温度,你可以写一个测试,调用你的函数,然后检查 self.temperature 是否是 'float' 类型,并且在一个合理的范围内(比如 -30 到 50 之间)。这样你就可以放心,API和你的函数都按预期工作。

撰写回答