神啊,求你賜給我平靜的心,去接受我無(wú)法改變的事;賜給我勇氣,去做我能改變的事;賜給我智慧,去分辨兩者的不同。 --平靜之禱
追到一個(gè)心儀的女生不難,難于如何保持和培養(yǎng)一份真摯的感情;獲得一時(shí)的財(cái)富也不難,難于如何長(zhǎng)久保持收益;創(chuàng)業(yè)的公司很容易博得一時(shí)媒體的關(guān)注以及某次天使的投資,但難于如何排除各種障礙、充分利用各方資源發(fā)展成中企業(yè)及至上市公司。
同樣,提供一時(shí)的接口很容易,但當(dāng)我們需要不斷為接口提供升級(jí),以及當(dāng)我們維護(hù)提供一整套接口時(shí),面臨的困難和問題會(huì)越來(lái)越大。
所以,這是一場(chǎng)持久的戰(zhàn)役。需要我們用穩(wěn)重的心態(tài)、專業(yè)的能力在背后持久支撐、推動(dòng)。
值得慶幸的是,這些都是問題而不是限制,都是可以被解決的。
以下是結(jié)合 @郭了個(gè)浩浩 同學(xué)提供的apigee.web_api.pdf文檔,以及我們多年來(lái)的項(xiàng)目實(shí)際開發(fā)經(jīng)驗(yàn)為新手提供的一些建議,對(duì)老同學(xué)相信也會(huì)有所幫助。
每個(gè)建議通常會(huì)包括三部分: 現(xiàn)在主流的做法、PhalApi的做法以及項(xiàng)目的選取。
為了大家查閱和翻看,這里先羅列本章的全部建議:
目前,后臺(tái)接口開發(fā)可以用RESTFull風(fēng)格,也可以用Web Service;可以用SOAP協(xié)議、RPC協(xié)議,也可以用HTTP協(xié)議;可以用短鏈接,也可以使用長(zhǎng)鏈接。如果我們希望繼續(xù)進(jìn)行劃分,還可以分為同步或異步、單個(gè)或批量、是否有SDK包、內(nèi)部接口還是開放接口平臺(tái)等。
現(xiàn)在看來(lái),大部分大型的企業(yè)以及大多數(shù)的小公司使用的都是HTTP協(xié)議下的接口開發(fā),部分使用RESTFull,但Web Service較少。如:
我們選取了HTTP的協(xié)議,在于其無(wú)論是客戶端接入、開發(fā)調(diào)試,還是部署構(gòu)建上都很容易實(shí)現(xiàn),而且也符合主流,因?yàn)榇蠹叶急容^熟悉。
這一點(diǎn)是非常重要的:因?yàn)楹?jiǎn)單,后臺(tái)接口開發(fā)的同學(xué)才會(huì)更容易上手;因?yàn)槿菀?,客戶端接入才?huì)更加無(wú)壓力而不用擔(dān)心處處受挫。
根據(jù)項(xiàng)目不同的項(xiàng)目背景和需求,可以選擇你合適的風(fēng)格或者協(xié)議。但是即使出于安全、性能或者其他技術(shù)或非技術(shù)的原因而不采用HTTP協(xié)議的情況下,你也可以在PhalApi原有的接口開發(fā)實(shí)現(xiàn)時(shí),輕松擴(kuò)展你需要的協(xié)議。如使用SOAP,PHPRpc或者swoole下的TCP協(xié)議。其中,部分協(xié)議已有擴(kuò)展類庫(kù)提供支持。
首先,有一點(diǎn)是可以肯定的。
接口系統(tǒng)應(yīng)該有自己?jiǎn)为?dú)的域名,而不應(yīng)該附屬于網(wǎng)站或者管理后臺(tái)。
顯然,主流做法也是這樣做的。如:
如果可以,盡量讓接口系統(tǒng)使用獨(dú)立的域名,并且使用api作為一級(jí)域名。如:
//你的網(wǎng)站為:
http://www.demo.com
//則對(duì)應(yīng)的接口為:
http://api.demo.com
對(duì)于接口的異常處理,在使用HTTP協(xié)議下,可以通過HTTP本身的響應(yīng)狀態(tài)碼來(lái)進(jìn)行區(qū)分。在非HTTP協(xié)議并有SDK包的情況下,異常的處理手段則會(huì)更為多樣。
優(yōu)酷接口采用了HTTP響應(yīng)狀態(tài)碼加結(jié)果返回的形式,如:
Request URL:https://openapi.youku.com/v2/videos/show_basic.json
Request Method:GET
Status Code:400 Bad Request
{"error":{"code":1004,"type":"SystemException","description":"Client id null"}}
新浪微博也一樣:
Request URL:https://api.weibo.com/2/statuses/mentions/ids.json
Request Method:GET
Status Code:403 Forbidden
{"error":"auth by Null spi!","error_code":21301,"request":"/2/statuses/mentions/ids.json"}
微信接口則采用了統(tǒng)一200的形式,如:
Request URL:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=
Request Method:GET
Status Code:200 OK
{"errcode":41002,"errmsg":"appid missing"}
為了與HTTP保持一致性,同時(shí)降低不必要的復(fù)雜性,我們采用了200/400/500三大接口結(jié)果狀態(tài)碼。
注意,這里所說(shuō)的三大狀態(tài)碼,是指接口返回結(jié)果中的狀態(tài)碼,而不是HTTP的響應(yīng)狀態(tài)。
也就是說(shuō)接口全部的結(jié)果返回都應(yīng)該是200,除非接口服務(wù)有內(nèi)部未捕獲的異常,即:
Status Code:200 OK
返回結(jié)果狀態(tài)碼剛是以下幾種:
//正常返回
{
"ret": 200,
"data": {
//...
},
"msg": ""
}
//客戶端非法請(qǐng)求
{
"ret": 400,
"data": [],
"msg": "非法請(qǐng)求:接口服務(wù)Default.Test不存在"
}
//服務(wù)端內(nèi)部錯(cuò)誤
{
"ret": 500,
"data": [],
"msg": "服務(wù)器運(yùn)行錯(cuò)誤: can not connect to database db_demo"
}
####項(xiàng)目的選取
你可以根據(jù)你的需要,擴(kuò)展400和500這兩系列的錯(cuò)誤,如401表示登錄失敗等。
此外,在data里面,你也可以添加一個(gè)code來(lái)表示業(yè)務(wù)級(jí)的操作碼,以及客戶端根據(jù)不同的業(yè)務(wù)場(chǎng)景做出不同和反應(yīng)、交互或引導(dǎo)提示。
對(duì)外的命名,是指外部看得到的命名,如接口參數(shù)的名字,接口返回的結(jié)果節(jié)點(diǎn)名字,以及數(shù)據(jù)庫(kù)的表名、字段名。
新浪微博采用了小寫加下劃線的做法,如:
//URL
https://c.api.weibo.com/2/friendships/followers/trend_count.json
//請(qǐng)求參數(shù)
source
access_token
//返回結(jié)果
{
"uid": 10438,
"result": [
{
"days": "2012-04-04",
"follower_count_online":"15", //粉絲數(shù)
"active_follower":"14", //活躍粉絲數(shù)
"loyal_follower":"0" //互動(dòng)粉絲數(shù)
},
....
]
}
Amazon采用了首字母大寫且無(wú)下劃線的做法,如:
//Responses
HTTP/1.1 200 OK
Date: Wed, 25 Nov 2009 12:00:00 GMT
Connection: close
Server: AmazonS3
<?xml version="1.0" encoding="UTF-8"?>
<BucketLoggingStatus xmlns="http://doc.s3.amazonaws.com/2006-03-01">
<LoggingEnabled>
<TargetBucket>mybucketlogs</TargetBucket>
<TargetPrefix>mybucket-access_log-/</TargetPrefix>
<TargetGrants>
<Grant>
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="AmazonCustomerByEmail">
<EmailAddress>user@company.com</EmailAddress>
</Grantee>
<Permission>READ</Permission>
</Grant>
</TargetGrants>
</LoggingEnabled>
</BucketLoggingStatus>
我們提倡使用全部小寫加下劃線的命名,因?yàn)檫@樣更符合客戶端的使用,如:接口參數(shù):
//正確的
&user_id=888
//錯(cuò)誤的
&userId=888
返回字段:
//正確的
"device_type": "cube",
//錯(cuò)誤的
"deviceType": "cube",
數(shù)據(jù)庫(kù)字段:
//正確的
`user_id` bigint(20) DEFAULT '0' COMMENT '創(chuàng)建者的用戶ID',
//錯(cuò)誤的
`userId` bigint(20) DEFAULT '0' COMMENT '創(chuàng)建者的用戶ID',
不管是使用全部小寫,還是全部大寫,項(xiàng)目都應(yīng)該保持一致的命名風(fēng)格,而不是混合凌亂的風(fēng)格。
與對(duì)外命名對(duì)應(yīng)的則是對(duì)內(nèi)的命名規(guī)則,這里又回歸到了老生常談的PHP代碼風(fēng)格。
這里不作過多的說(shuō)明,只是稍作提及。
我們建議使用PEAR包的命名風(fēng)格,和駝峰法,如下為一個(gè)接口示例:
$ vim ./Api/Default.php
<?php
/**
* 默認(rèn)接口服務(wù)類
*
* @author: dogstar <chanzonghuang@gmail.com> 2014-10-04
*/
class Api_Default extends PhalApi_Api {
public function getRules() {
return array(
'index' => array(
'username' => array('name' => 'username', 'default' => 'PHPer', ),
),
);
}
public function index() {
return array(
'title' => 'Default Api',
'content' => T('Hello {name}, Welcome to use PhalApi!', array('name' => $this->username)),
'version' => PHALAPI_VERSION,
'time' => $_SERVER['REQUEST_TIME'],
);
}
}
你可以選擇你喜歡的風(fēng)格,但團(tuán)隊(duì)?wèi)?yīng)該保持一致。
即便你不喜歡PhalApi約定的PEAR命名,你也可以自行實(shí)現(xiàn)內(nèi)部的類加載機(jī)制。
當(dāng)有多個(gè)項(xiàng)目或者多個(gè)模塊并存時(shí),可以添加模塊名前綴來(lái)作區(qū)分,如:
$ tree
.
├── Demo
│ └── Api
│ └── DUser.php
├── MyApp
│ └── Api
│ └── MUser.php
└── Task
└── Api
└── TUser.php
$ head */*/*
==> Demo/Api/DUser.php <==
<?php
class Api_DUser extends PhalApi_Api {
}
==> MyApp/Api/MUser.php <==
<?php
class Api_MUser extends PhalApi_Api {
}
==> Task/Api/TUser.php <==
<?php
class Api_TUser extends PhalApi_Api {
}
其它的Domain層和Model層等也類似,這樣可以避免類名沖突,或者IDE開發(fā)環(huán)境下的混淆。
既然采用HTTP協(xié)議,那么安全方面就需要接口自身進(jìn)行保證。
所幸,現(xiàn)在可用的加密手段有多種選擇。
對(duì)于接口簽名,我們可以使用非對(duì)稱的驗(yàn)簽方式,如md5;也可以用對(duì)稱的方式,如RSA。
最后,為每一個(gè)接入的客戶端分配app_key和app_secrect即可。
當(dāng)然,更好的安全是接口系統(tǒng)再提供登錄態(tài)的驗(yàn)證,即通常所說(shuō)的token。這兩者的相合,會(huì)為接口增加更好的安全保障。
我們不提供具體的接口簽名方案,是因?yàn)榘堰@種決策移交給項(xiàng)目應(yīng)用本身進(jìn)行定制。
而定制也是非常簡(jiǎn)單的,只需要簡(jiǎn)單的兩步即可:
對(duì)于token,雖然框架沒有提供內(nèi)置的實(shí)現(xiàn),但可以從PhalApi的擴(kuò)展類庫(kù)尋找這種支持,這一點(diǎn)已經(jīng)User擴(kuò)展類庫(kù)支持。
正如PhalApi提供的自由空間,項(xiàng)目可以自行實(shí)現(xiàn)接口簽名,和根據(jù)需要是否采用User擴(kuò)展類庫(kù),或者自行實(shí)現(xiàn)token的處理。
在 [1.14.1 統(tǒng)一返回的格式]一節(jié)中,已經(jīng)對(duì)JSON的返回格式作了說(shuō)明,這里不再贅述,也只是稍作提及。
目前采用了JSON的格式返回的有:
采用了XML格式返回的有:
我們默認(rèn)采了JSON的格式返回。
項(xiàng)目可以輕松擴(kuò)展成其他格式的返回。
先從項(xiàng)目?jī)?nèi)部的文件劃分說(shuō)起,通常最為常見的情況是,很多開發(fā)人員都喜歡把很多很多很多接口都塞到一個(gè)接口文件里面。
這樣的文件,通常會(huì)有2K到3K左右。
我覺得這是一種極端,而且是一種不好的極端。因?yàn)槲募^大的話,會(huì)帶來(lái)很多問題。
但與之對(duì)立的有另一種做法,即一個(gè)文件,一個(gè)接口。
這一點(diǎn),在我之前就職的一家出名的游戲公司中得到了廣泛的認(rèn)可和遵循。如:
//?service=UserInfo.Go
<?php
class Api_UserInfo extends PhalApi_Api {
public function go() {
//TODO
}
}
//?service=GroupInfo.Go
<?php
class Api_GroupInfo extends PhalApi_Api {
public function go() {
//TODO
}
}
雖然也是一種極端,但卻很好地做到了接口隔離,即不用擔(dān)心修改此接口的實(shí)現(xiàn)而影響到其他接口服務(wù)。
最后,我們?cè)賮?lái)聊URL規(guī)則,就更順暢了。如果我們采用一個(gè)文件對(duì)應(yīng)一個(gè)接口,則我們可以省略Action(全部都為go()方法),簡(jiǎn)寫成:?service=XXX。
再進(jìn)一步,我們可以利用接口服務(wù)器(如Nginx)的規(guī)則Rewrite來(lái)提供更好的URL規(guī)則,同時(shí)盡量隱藏我們的接口內(nèi)部實(shí)現(xiàn)細(xì)節(jié),如:
//原始地
http://api.demo.com/?service=UserInfo.Go
//簡(jiǎn)化地
http://api.demo.com/?service=UserInfo
//再進(jìn)一步
http://api.demo.com/UserInfo
//或者
http://api.demo.com/UserInfo.json
還有一點(diǎn)需要關(guān)注的就是接口的版本,當(dāng)有v1,v2,v3等不同的版本時(shí),我們也需要在接口URL中體現(xiàn)這些版本的不同。
目前而言,PhalApi在URL規(guī)則和路由這塊還比較欠缺,沒有像其他網(wǎng)站一樣提供強(qiáng)大的路由支持。
但我們?cè)诖a實(shí)現(xiàn)的層面,可以提供不同的入口,以開放給不同的終端(內(nèi)部的或者外部的), 以及不同的版本支持。如:
$ tree Public/
Public/
├── v1
│ └── index.php
├── v2
│ └── index.php
└── v3
└── index.php
3 directories, 3 files
則對(duì)應(yīng)的版本URL則可以為:
//v1版本
http://api.demo.com/v1/?service=Default.Index
//v2版本
http://api.demo.com/v2/?service=Default.Index
//v3版本
http://api.demo.com/v3/?service=Default.Index
項(xiàng)目可以結(jié)合不同的入口,以及接口服務(wù)器的URL規(guī)則Rewrite作一些自定的URL路由。
目前移動(dòng)開發(fā)主要有iOS、Android、Windowns Phone、網(wǎng)站等不同的終端,各種終端又有不同的語(yǔ)言,如果我們需要提供SDK包,不僅僅需要考慮到縱向的版本升級(jí),還需要維護(hù)橫向的多樣性。
而且,如果我們使用的是HTTP協(xié)議,則不必要擔(dān)心這些維護(hù)的成本,同時(shí)給客戶端提供一個(gè)自由的空間進(jìn)行調(diào)用 -- 即客戶端可以自己編寫本身的接口客戶端。
很多國(guó)內(nèi)的開放平臺(tái)接口都是不提供SDK包的,但有些安全度高的則會(huì),如支持寶。
以下是一些提供了SDK的平臺(tái) :
我們暫時(shí)沒有提供SDK包,但對(duì)于PHP,有一個(gè)簡(jiǎn)單的客戶端類,可見: [1.13]-統(tǒng)一的接口請(qǐng)求方式
出于公司產(chǎn)品簇的項(xiàng)目考慮,項(xiàng)目可以內(nèi)部提供SDK給同類的客戶端使用,如分為iOS版的客戶端SDK,以及Android版的客戶端SDK。
有一點(diǎn)是非常重要的,千萬(wàn)不要讓不懂PHP語(yǔ)言的人去開發(fā)提供PHP的SDK包,更不要使用所謂的工具自動(dòng)轉(zhuǎn)換生成SDK包代碼。
在我曾經(jīng)做過的一個(gè)項(xiàng)目中,因?yàn)樾枰尤胍粋€(gè)接口系統(tǒng),而這個(gè)接口是由專業(yè)的JAVA團(tuán)隊(duì)維護(hù)的,但他們對(duì)PHP語(yǔ)言則是非常薄弱,以致他們使用了工具來(lái)生成PHP語(yǔ)言的SDK包。
這就導(dǎo)致了我在接入一個(gè)簡(jiǎn)單的接口時(shí),卻開發(fā)聯(lián)調(diào)耗費(fèi)了兩天、測(cè)試聯(lián)調(diào)時(shí)耗費(fèi)了在接口調(diào)用超時(shí)問題排查上。
而最后找到的原因卻是因?yàn)閍pp_key不對(duì)而導(dǎo)致服務(wù)端異常,而在SDK包卻隱藏了這一異常錯(cuò)誤信息,反而給出了time out超時(shí)的提示,嚴(yán)重誤導(dǎo)了排查的方向!
而當(dāng)我嘗試深入去調(diào)試SDK時(shí),得到卻又是既沒有code又沒有message的異常!最讓人難以忍受的是,他們提供的SDK包竟然和JAVA的企業(yè)系統(tǒng)一樣復(fù)雜的結(jié)構(gòu)(正如他們是使用工具來(lái)生成轉(zhuǎn)換的)!
想象一下,PHP代碼下有\(zhòng)com\sina\webo\sdk\Constants.php這樣類似JAVA的文件結(jié)構(gòu),PHP的同學(xué)會(huì)作何感想?用JAVA的世界的方式來(lái)開發(fā)PHP,顯然是走不通的啊!
而執(zhí)意要走的話,到最后就是各種接入的痛苦,稍微按奈不住的同學(xué)難免就會(huì)因?yàn)榍榫w問題而大開爭(zhēng)論了。而這一切,只是因?yàn)榉荘HP人員使用了自動(dòng)生成工具。
我覺得,這是一種不負(fù)責(zé)任的做法,希望大家不要效仿。
(場(chǎng)外音:通過沐浴法理清了頭緒,繼續(xù)回來(lái)執(zhí)筆編寫)。
就我個(gè)人經(jīng)歷而言,markdown就是一個(gè)開始你會(huì)拒絕,接著你會(huì)越來(lái)越喜歡,到最后會(huì)愛不釋手的一個(gè)工具。
如果你或者你的團(tuán)隊(duì)還在使用郵件或者work文檔來(lái)傳遞共享接口文檔,那就太不應(yīng)該了;如果你正在使用某個(gè)WIKI系統(tǒng)進(jìn)行文檔的維護(hù)但卻不喜歡它的編輯或者展示方式時(shí),你可以嘗試使用一下markdown。
正如你現(xiàn)在正在查看的文檔也是通過markdown編寫的。
作為開放接口平臺(tái),文檔肯定是以網(wǎng)站的形式提供。但很多時(shí)候,對(duì)于我們內(nèi)部的接口或者小項(xiàng)目來(lái)說(shuō),顯然這樣的成本太大了。
接口,從簡(jiǎn)單開始。
我們理應(yīng)一直堅(jiān)持這一點(diǎn),所以文檔也是一樣,我們應(yīng)該尋求一種在內(nèi)部快速共享最新接口文檔的途徑。如:
你可以根據(jù)項(xiàng)目的需要,或者公司以往的做法,但至少不要再使用郵件或者word文檔。
單元測(cè)試,在PhalApi里面不只一次提到了,這里再次進(jìn)行說(shuō)明,是希望能引起大家的關(guān)注,去嘗試體驗(yàn)一下。
我們都知道,在開發(fā)一個(gè)新功能時(shí)、新接口時(shí),修復(fù)一個(gè)BUG或者作一些大的調(diào)整或者重構(gòu)工作,我們是毫無(wú)壓力的,而且這時(shí)的成本很低,僅在于開發(fā)人員本身的時(shí)間和精力的消耗。
當(dāng)提測(cè)后進(jìn)入測(cè)試階段,測(cè)試人員發(fā)現(xiàn)一個(gè)BUG后,有些團(tuán)隊(duì)會(huì)以禪道或者Bugzilla或其他方式來(lái)紀(jì)錄和追蹤BUG。這時(shí)我們開發(fā)會(huì)覺得一個(gè)這么小的問題還需要去紀(jì)錄、去登記很不值得。然后,我們應(yīng)當(dāng)注意到這時(shí)修復(fù)一個(gè)BUG會(huì)涉及到測(cè)試人員資源的開銷。
當(dāng)進(jìn)入了回歸測(cè)試階段,特別是多系統(tǒng)交互、跨團(tuán)隊(duì)合作時(shí),一個(gè)BUG就會(huì)從一個(gè)人傳到另一個(gè)人,從這個(gè)團(tuán)隊(duì)流到那個(gè)團(tuán)隊(duì),這時(shí)成本就會(huì)逐漸增大。
最后,上線后,當(dāng)一個(gè)奇怪的問題出現(xiàn)后,我們需要定位原因就更加困難重重了。
我曾經(jīng)就經(jīng)歷這樣一番:有用戶發(fā)現(xiàn)游戲的道具減少了。我們一開始以為是某些運(yùn)營(yíng)配置、或者數(shù)據(jù)以及用戶的等級(jí)限制所引發(fā)的,但在排除了各種業(yè)務(wù)的問題后,到最后卻發(fā)現(xiàn)是PHP中array使用“+”運(yùn)算而引發(fā)的血案!
在正常情況下,我們都知道array_merge()函數(shù)對(duì)于數(shù)值的下標(biāo)則會(huì)追加并重新生成下標(biāo)序列,即會(huì)合并;而數(shù)組+則會(huì)去掉相同下標(biāo)的元素。
但實(shí)際情況下,線上BUG所產(chǎn)生的影響不在于排查和修復(fù)的時(shí)間成本,而在于在這段時(shí)間內(nèi)所損失的金額、數(shù)據(jù)等成本。
當(dāng)然,從測(cè)試的角度上看,測(cè)試并不能保證我們的系統(tǒng)沒有BUG,只能說(shuō)暫時(shí)未發(fā)現(xiàn)BUG。
單元測(cè)試也一樣,作為開發(fā)人員,我們應(yīng)當(dāng)在最低成本的時(shí)期就及時(shí)發(fā)現(xiàn)我們直覺覺得可能會(huì)出現(xiàn)的問題并進(jìn)行修復(fù)。
對(duì)我們親手所編寫的代碼負(fù)責(zé),并且用客觀的方式來(lái)證明我們的代碼目前未發(fā)現(xiàn)問題,而不是主觀認(rèn)為“我寫的代碼沒有問題”。更不應(yīng)該一次又一次地犯下各種低級(jí)或者重復(fù)的錯(cuò)誤,而讓團(tuán)隊(duì)其他成員對(duì)我們喪失信任。
PhalApi一直很注重單元測(cè)試,也很注重自動(dòng)化,為了減輕大家重復(fù)編寫單元測(cè)試骨架代碼的痛苦,我們提供了一個(gè)可以生成單元測(cè)試代碼的腳本。
假設(shè)我們有這么一個(gè)類:
<?php
class Api_Default extends PhalApi_Api {
public function index() {
//TODO
}
}
那么,我們可以這樣生成測(cè)試代碼:
$ cd .//Demo/Tests
$ phalapi-buildtest ../Api/Default.php Api_Default ./test_env.php
<?php
/**
* PhpUnderControl_ApiDefault_Test
*
* 針對(duì) ../Api/Default.php Api_Default 類的PHPUnit單元測(cè)試
*
* @author: dogstar 20150514
*/
require_once dirname(__FILE__) . '/test_env.php';
if (!class_exists('Api_Default')) {
require dirname(__FILE__) . '/../Api/Default.php';
}
class PhpUnderControl_ApiDefault_Test extends PHPUnit_Framework_TestCase
{
public $apiDefault;
protected function setUp()
{
parent::setUp();
$this->apiDefault = new Api_Default();
}
protected function tearDown()
{
}
/**
* @group testGetRules
*/
public function testGetRules()
{
$rs = $this->apiDefault->getRules();
}
/**
* @group testIndex
*/
public function testIndex()
{
$rs = $this->apiDefault->index();
}
}
溫馨提示:
- 可以先執(zhí)行:ln -s /path/to/PhalApi/phalapi-buildtest /usr/bin/phalapi-buildtest
- test_env.php為測(cè)試環(huán)境初始化文件,可以在里面引用init.php文件,并作一些調(diào)整
- 輸出的測(cè)試代碼可以重定向到./Demo/Tests/Api/Api_Default_Test.php,讓測(cè)試代碼與產(chǎn)品代碼對(duì)齊
最后,我們就可以這樣執(zhí)行單元測(cè)試了:
$ phpunit ./Api_Default_Test.php
更多建議: