前后端分离几乎已经是业界对开发和部署方式所达成的一种共识,今天简单介绍一下 Swagger 吧,以 PHP + Yii 为例。

(没错就是这货:)

Swagger

Editor

在开始之前,先来安装一个编辑器,Swagger Editor,一次执行下面三条指令:


git clone https://github.com/swagger-api/swagger-editor.git

cd swagger-editor

npm start

(如果遇到问题,请先确认是否已经安装了 Node.js)

此外,为了方便你也可以使用网页版: https://editor.swagger.io/

Usage

  • Swagger-UI 下载

git clone https://github.com/swagger-api/swagger-ui.git

下载好之后进入 dist 目录,将 index.html 中的 url = "https://petstore.swagger.io/v2/swagger.json"; 的值改为你自己的:


<script type="text/javascript">
  $(function () {
        var url = window.location.search.match(/url=([^&]+)/);
        if (url && url.length > 1) {
          url = decodeURIComponent(url[1]);
        } else {
        // 这里:
          url = "https://localhost/yii2/swagger-docs/swagger.json";
        }

根据需要,你也可以调整其他代码,例如修改语言:


<script src='lang/translator.js' type='text/javascript'></script>
<!-- <script src='lang/ru.js' type='text/javascript'></script> -->
<script src='lang/zh-cn.js' type='text/javascript'></script>

好了,现在我们打开链接(例如 https://yourdomain/yii2/swagger-ui/dist/index.html)就可以进入 Swagger 页面了,但因为我们还没有 swagger.json 所以现在还没有什么内容。

现在再来处理后端的事务:


git clone https://github.com/zircote/swagger-php.git

Yii 2 为例,我们使用 composer:


composer require zircote/swagger-php

然后 composer update 即可。

接下来我们用示例数据生成一下。


php swagger.phar Examples -o doc

其中 -o 后面的内容是要生成到的路径,例如:


php ./vendor/zircote/swagger-php/bin/swagger ./vendor/zircote/swagger-php/Examples -o ~/swagger-docs-demo

现在我们再去查看 index.html 就可以看到数据的。

做完了准备工作之后,只要写好代码注释就行了,具体细节请大家参考 官方文档

举个栗子:


/**
* @SWG\Model(
* id="vps",
* required="['type', 'name']",
*  @SWG\Property(name="name", type="string"),
*  @SWG\Property(name="label", type="string"),
*  @SWG\Property(name="type", type="string", enum="['vps', 'dedicated']")
* )
*/
class HostVPS extends Host implements ResourceInterface {
    // code here...
}

为了避免每次修改注释都要手动生成 JSON 的麻烦,我们可以在跳转到 UI 页面之前先访问一个控制器来生成 JSON,例如:


$b2broot = Yii::getAlias('@b2broot');
$swagger = \Swagger\scan($b2broot.'/myapi');
$json_file = $b2broot.'/swagger-docs/swagger.json';
$is_write = file_put_contents($json_file, $swagger);

if ($is_write == true) {
   $this->redirect('https://yourdomain/yii2/swagger-ui/dist/index.html');
}

Integration Contract Test

前后端分离不可避免的会带来集成问题,但关于契约测试的内容不在这篇博客的主题范畴内,有兴趣可以参考 下面这篇文章:


[转] Integration Contract Test

One of the most common cases of using a TestDouble is when you are communicating with an external service. Typically such services are being maintained by a different team, they may be subject to slow, and unreliable networks, and maybe unreliable themselves. That’s why a test double is handy, it stops your own tests from being slow and unreliable. But testing against a double always raises the question of whether the double is indeed an accurate representation of the external service, and what happens if the external service changes its contract?

image

A good way to deal with this is to run your own tests against the double, but to periodically run a separate set of integration contract tests that checks all the calls against your test doubles return the same results as a call to the external service would. A failure in any of these integration contract tests implies you need to update your test doubles, and probably your code to take into account the service contract change.

These tests need not be run as part of your regular deployment pipeline. Your regular pipeline is based on the rhythm of changes to your code, but these tests need to be based on the rhythm of changes to the external service. Often running just once a day is plenty.

A failure in an integration contract test shouldn’t necessarily break the build in the same way that a normal test failure would. It should, however, trigger a task to get things consistent again. This may involve updating the tests and code to bring them back into consistency with the external service. Just as likely it will trigger a conversation with the keepers of the external service to talk about the change and alert them to how their changes are affecting other applications.

This communication with the external service supplier is even more important if this service is being used as part of a production application. In these cases an integration contract change may break a production application, triggering an emergency fix and an urgent conversation with the supplier team.

To reduce the chances of unexpected breaks in integration contracts, it’s useful to move to a Consumer Driven Contracts approach. You can facilitate this by letting the supplier team have copies of your integration contract tests so they can run them as part of their build pipeline.

When testing an external service like this, it’s usually best to do so against a test instance of the external service. Occasionally you’ll have no choice but to hit the production instance, at that point you’ll need to talk to the suppliers to let them know what’s happening and be extra careful with what the tests do.

Integration contract tests check the contract of external service calls, but not necessarily the exact data. Often a stub will snapshot a response as at a particular date, since the format of the data matters rather than the actual data. In this case the integration contract test needs to check that the format is the same, even if the actual data has changed.

One of the best way to build these test doubles is to use a SelfInitializingFake.