zoukankan      html  css  js  c++  java
  • 谈 API 的撰写

    在做一个系统时,有一些子系统几乎是必备的:配置管理,CLI,以及测试框架。

    配置管理

    我们先说配置管理。一个系统的灵活度,和它的配置管理是离不开的。系统中存在的大量的预置的属性(下文简称 property),需要有一个公共的地方来放置。这里我不说「常量」,而是说「预置的属性」,是因为这属性可能需要在运行时发生改变,而常量的范畴会让人有所误解。

    最简单的配置管理就是把所有的 property 放在一个配置文件中,在系统启动的时候读入。配置文件的类型有很多选择:ini,json,yaml,toml 等。这些类型各有优劣,选择的时候注意配置文件最好能够支持注释,便于维护。从这个角度看,json 不是个太好的选择。toml 可能大家用得不多,它是 github 创始人 Tom 定义的一种格式,类似于 ini 但灵活不少,感兴趣的可以在 github 里搜索 toml。

    我们知道,一个项目会有多种运行时:development,staging,production,test。不同的运行时加载的配置文件可能不同。所以配置管理需要考虑这一点,让配置文件可以重载(override)。最常见的重载策略是系统提供一个公共的配置文件:default,然后各种运行时相关的配置文件继承并局部重载这个配置。在系统启动的时候,二者合并。

    有些时候,我们需要在系统运行的时候改写配置。由于配置一般在系统初始化的时候就被读入内存,所以单纯改写配置文件无法即时生效,这时,你需要像管理缓存一样去管理和配置相关的数据,将其封装在一个容器里:当配置被修改时,调用这个容器的 invalidate 方法 —— 这样,下次访问任意一个配置项时,会重新读入配置,并缓存起来。

    对于分布式的项目,配置应该集中存储在诸如 redis 这样的系统,以方便统一处理(orchestrate)。

    CLI

    写 CLI 并非难事,但一个 CLI 子系统的难点是:

    • CLI 的发现和自注册。你的 framework 的用户只要遵循某种 convention 撰写 CLI,这些 CLI 就会被自动集成到系统里。

    • CLI 的撰写者能够轻松地获取到系统的信息,也就是说,系统有自省(introspection)的能力。

    前者的实现我们在前面的篇章里(谈谈编译和运行)讲路由是如何注册的已经提到,这里就不赘述。后者非常重要,在展开讨论之前,我们先考虑一个问题:做一个系统的过程中,我们希望这个系统的 CLI 解决什么问题?

    首先,CLI 显然不是给用户用的,是给程序员用的,所以,CLI 提供一些简化程序员工作的脚本。那么,作为一个 API 系统,程序员都需要哪些 CLI 呢?我们看一些例子:

    • 创建某些 skeleton - rails / django 都有新建项目,新建 model / controller 等的 CLI

    • 获取系统的信息。比如:不用看代码就能很快知道系统里都有哪些 route,哪些 model 等。

    • 生成某些信息或者模拟某些行为。当你调试你的系统时,每次生成某种状态很烦人,比如说登录,可以通过 CLI 一键完成。

    • ...

    这些例子大部分都需要系统的自省的能力,比如说下面这个 CLI:

    在这个 CLI 的执行函数里,我们使用了这些系统信息:

    • 系统中所有 app 的名字。

    • 某个 app 的 instance。

    • app 的配置信息。

    • app 启用的 middleware。

    • app 的所有的路由。

    • ...

    如果我们无法在系统的非运行时获取这些信息,那么,CLI 的威力会大打折扣。这也印证了我之前的文章 里所述的将「编译时」和「运行时」分开的重要性。很多框架,如 express.js,由于无法很清晰地将二者区分开,以至于你想在非运行的时刻获取 route / middleware 的信息,非常困难。

    测试框架

    API 的测试是相当无趣的(几乎所有的测试例撰写起来都相当无趣),但是测试的重要性是不容置疑的,尤其对于一个不断重构的代码。如果说别的系统的测试只能在局部寻找规律而进行优化,API 的测试,尤其是 functional testing 是可以全局考虑的。

    比如你有一个 API 是 PUT /feature/:id,要测试这个 API 是否工作正常,你大概会考虑这些测试例:

    • PUT 正确的数据到一个错误的 id,测试是否会出错;

    • PUT 错误的 etag,测试 concurrent udpate 是否工作;

    • PUT 空数据,测试 validator 是否正常工作;

    • PUT 错误的数据,测试 validator 是否正常工作;

    • PUT 正常的数据,测试基本功能是否工作。

    这些测试例有这些共同之处:

    • 需要运行一个 temporary server

    • 需要发送请求到 temporary server 上

    • 需要检测 status code,以及 response header / body 来确认是否出现期待的结果

    如果每个测试都写一个测试例,虽然每个的代码量并不太大,但测试一个 API 就需要 5 个测试,API 的规模一上,代码量就大了,添加和维护都很麻烦。

    我们可以定义一种针对于此的测试语言来描述测试的 fixture:

    这个定义非常简单,相信大家都能看明白:

    • 测试的描述(用于 test report)

    • 测试所用的 url

    • 期待的结果,包括 status code,headers 和 body

    这里面,我们用了一种很简单的方式区分 field name 和函数。比如 body 下面的 #length,它的结果不是body['length'] 而是 length(body),前者虽然对 array 有效(javascript),但对 object 无效。

    要运行这样的 fixture,并不需要撰写太多的代码(假设我们是用 ava 作为测试工具):

    这里面,runAssertion 发送 request,并对比 fixture 里面的数据和 response,来确定一个 test case pass 或者 fail。

    这样下来,我们成功地把繁琐的 test case 的撰写转化成一个 parser 和一系列 fixture 的撰写。parser 的撰写是一次性的,以后改动很少(但会添加新的功能,比如新的函数 - 如上的 #xxx),而 fixture 的撰写对比着之前的例子,几乎很难出错。这样的测试例,你三五分钟写出一个来是轻而易举的事情。

  • 相关阅读:
    Leetcode NO.110 Balanced Binary Tree 平衡二叉树
    Leetcode NO.226 Invert Binary Tree 翻转二叉树
    Leetcode NO.215 Kth Largest Element In An Array 数组中的第K个最大元素
    根据特征的浏览器判断
    Cygwin在打开在当前目录
    【转帖】科学对待 健康养猫 打造快乐孕妇
    解决chrome浏览器安装扩展、应用程序一直处在“检查中”的问题
    对【SQL SERVER 分布式事务解决方案】的心得补充
    关于“点击这里继续访问您选择的百度XXX”
    VBA一例:如何保持文本框焦点
  • 原文地址:https://www.cnblogs.com/annie00/p/5773159.html
Copyright © 2011-2022 走看看