這是一個系列文章呀袱,分3部分:
1稚照、準(zhǔn)備工作--Yii2高級模板的安裝鲫忍,以及編寫一個類(方法)和一個model的RETful
2冀墨、測試工作--單元測試和API(REST規(guī)范的)測試
3、力爭上游--覆蓋率献丑、基礎(chǔ)概念末捣、引用文獻(xiàn)等
本文是第二部分,主要是單元測試和API測試创橄。
單元測試
1箩做、執(zhí)行vendor/bin/codecept generate:cest -c common unit CustomeString
,
新建了common\tests\unit\CustomeStringCest.php
妥畏,開始寫測試代碼了邦邦,增加內(nèi)容:
public function tryToTest(UnitTester $I)
{
$len = 10;
$prefix = 'pre';
$string = CustomString::generateCode($len, $prefix);
expect(strlen($string))->equals($len + 3);
expect(substr($string, 0, 3))->equals($prefix);
//等效于 expect($string)->startsWith($prefix);
//這就是codeception的強(qiáng)大之處,封裝了幾乎所有測試需要的醉蚁,官方說90%+,且不希望你重復(fù)造輪子
}
2燃辖、執(zhí)行vendor/bin/codecept run -c common
。
我們可以在命令終端看到測試運(yùn)行的情況网棍。
至此郭赐,恭喜你,已經(jīng)利用codeception進(jìn)行了某個類的(方法的)測試确沸。
我們繼續(xù)來書寫如何測試一個model:
4捌锭、執(zhí)行vendor/bin/codecept generate:test -c common unit models/News
新建一個common\test\unit\models\NewsTest.php,我們仿照LoginFormTest.php
罗捎,編寫NewsTest.php:
<?php
namespace common\tests\models;
use common\fixtures\NewsFixture;
use common\models\News;
class NewsTest extends \Codeception\Test\Unit
{
public function _fixtures()
{
return [
'news' => [
'class' => NewsFixture::class,
'dataFile' => codecept_data_dir() . 'news.php',
],
];
}
public function testValidate()
{
$model = new News(['title' => 'one title']);
expect('model should create successfully', $model->save())->true();
$model = new News(['code' => \Yii::$app->security->generateRandomString(33)]);
expect('model should create fail', $model->save())->false();
expect('error message should be set', $model->errors)->hasKey('code');
$model = new News(['title' => null]);
expect('model should create fail', $model->save())->false();
expect('error message should be set', $model->errors)->hasKey('title');
$model = new News(['title' => \Yii::$app->security->generateRandomString(256)]);
expect('model should create fail', $model->save())->false();
expect('error message should be set', $model->errors)->hasKey('title');
}
public function testData()
{
$data = News::find()->where(['title' => 'This is a title'])->one();
expect(count($data))->equals(1);
}
}
5观谦、因?yàn)樾枰玫?code>NewsFixture::class和codecept_data_dir() . 'news.php'
,
所以我們需要在common的fixtures下新建NewFixture.php
(具體位置無所謂桨菜,只需要保證命名空間是common\fixtures
):
<?php
namespace common\fixtures;
use yii\test\ActiveFixture;
class NewsFixture extends ActiveFixture
{
public $modelClass = 'common\models\News';
}
然后在common\tests\_data
下新建news.php,自己添加一些數(shù)據(jù):
<?php
return [
[
'code' => 'abcdefg',
'title' => 'This is a title',
'content' => '<p>some Content</p>',
'status' => 0,
'created_at' => 1402312317,
'updated_at' => 1402312317,
'created_by' => 1,
'updated_by' => 1,
],
[
'code' => '22222',
'title' => 'This is second title',
'content' => '<p>some Content</p>',
'status' => 1,
'created_at' => 1402312317,
'updated_at' => 1402312317,
'created_by' => 1,
'updated_by' => 1,
],
];
6豁状、執(zhí)行vendor/bin/codecept run -c common
,查看測試運(yùn)行情況倒得。
至此泻红,單元測試算告一段落。
單元測試霞掺,基本上就是對自定義的類的(方法的)測試谊路,還有model的一些測試。
當(dāng)有新的自定義類或者model時菩彬,類似上述新增測試用例即可缠劝。
7、 補(bǔ)充:
由于codeception包含3種格式的測試文件:Cept, Cest and Test
Cept are written as narrative scenarios. To make a PHP file a valid scenario, its name should have a Cept suffix.
描述行語言骗灶,文件后綴是Cept惨恭,常用于功能、驗(yàn)收還有API測試耙旦。
Cest combines scenario-driven test approach with OOP design. In case you want to group a few testing scenarios into one, you should consider using Cest format.
Codeception can also execute PHPUnit test files for unit testing脱羡。
注意Test格式時,方法名需要以test
開頭免都,另外兩種格式不要求锉罐。
上述的例子是Test格式,當(dāng)然可以寫成Cept和Cest琴昆,實(shí)質(zhì)上等效氓鄙。
我習(xí)慣單元測試用Cest,API測試用Cept业舍。
執(zhí)行vendor/bin/codecept generate:cest -c common unit models/News
抖拦,
新建一個common\test\unit\models\NewsCest.php,等效代碼如下:
<?php
namespace common\tests\unit\models;
use common\fixtures\NewsFixture;
use common\models\News;
use common\tests\UnitTester;
class NewsCest
{
public function _before(UnitTester $I)
{
$I->haveFixtures([
'news' => [
'class' => NewsFixture::class,
'dataFile' => codecept_data_dir() . 'news.php',
],
]);
}
public function _after(UnitTester $I)
{
}
// tests
public function tryToTestValidate(UnitTester $I)
{
$model = new News(['title' => 'one title']);
expect('model should create successfully', $model->save())->true();
$model = new News(['code' => \Yii::$app->security->generateRandomString(33)]);
expect('model should create fail', $model->save())->false();
expect('error message should be set', $model->errors)->hasKey('code');
$model = new News(['title' => null]);
expect('model should create fail', $model->save())->false();
expect('error message should be set', $model->errors)->hasKey('title');
$model = new News(['title' => \Yii::$app->security->generateRandomString(256)]);
expect('model should create fail', $model->save())->false();
expect('error message should be set', $model->errors)->hasKey('title');
}
public function toTestData()
{
$data = News::find()->where(['title' => 'This is a title'])->one();
expect(count($data))->equals(1);
}
}
功能和驗(yàn)收測試
由于前后端分離舷暮,這里不再介紹codeception的功能和驗(yàn)收測試态罪。
你可以查看frontend/tests/下的functional和accptance部分。
關(guān)于前端測試下面,可以參看CodeceptJS
API測試
接口的自動化測試复颈,這里是一個方案,具體參看WebServices沥割,
Postman的自動化接口測試也是一個不錯的方案耗啦,有時間再整理凿菩。
首先要執(zhí)行vendor/bin/codecept g:suite api -c blog
,輸出:
Helper \blog\tests\Helper\Api was created in /home/lijun/works/yii-application/blog/tests/_support/Helper/Api.php
Actor ApiTester was created in /home/lijun/works/yii-application/blog/tests/_support/ApiTester.php
Suite config api.suite.yml was created.
Next steps:
1. Edit api.suite.yml to enable modules for this suite
2. Create first test with generate:cest testName ( or test|cept) command
3. Run tests of this suite with codecept run api command
Suite api generated
按照提示繼續(xù)下面的步驟:
1)帜讲、編輯api.suite.yml衅谷,啟用REST,且依賴Yii2似将。如下:
#suite_namespace: blog\tests\api
actor: ApiTester
modules:
enabled:
- \blog\tests\Helper\Api
- REST:
#url:
depends: Yii2
#part: Json
2)获黔、執(zhí)行vendor/bin/codecept g:cept api -c blog News
創(chuàng)建測試文件blog\tests\api\NewsCept.php。
<?php
use blog\tests\ApiTester;
use Codeception\Util\HttpCode;
$I = new ApiTester($scenario);
//create
$data = ['code' => 'code1', 'title' => 'first title', 'content' => 'first content'];
$I->wantTo('create a news');
$I->sendPOST('/news', $data);
$I->seeResponseCodeIs(HttpCode::CREATED);
$I->seeResponseIsJson();
//{"code":"code1","title":"first title","content":"first content","id":16}
$I->seeResponseContainsJson($data);
//view
$I->wantTo('view a news');
$id = $I->grabDataFromResponseByJsonPath('$.id')[0];
$I->sendGET('/news/' . $id);
$I->seeResponseCodeIs(HttpCode::OK);
$I->seeResponseIsJson();
$I->seeResponseMatchesJsonType([
'id' => 'integer',
'title' => 'string',
'content' => 'string|null',
]);
//update
$I->wantTo('update news');
$id = $I->grabDataFromResponseByJsonPath('$.id')[0];
$I->sendPUT('/news/' . $id, ['content' => 'updated content']);
$I->seeResponseCodeIs(HttpCode::OK);
$I->seeResponseContainsJson(['content' => 'updated content']);
//index
$I->wantTo('list news');
$I->sendGET('/news');
$I->seeResponseCodeIs(HttpCode::OK);
//{"items":[{"id":49,"code":"code1","title":"first title","content":"first content","status":0,"created_at":0,"created_by":0,"updated_at":0,"updated_by":0}],"_links":{"self":{"href":"http://localhost/index-test.php/news?page=1"}},"_meta":{"totalCount":1,"pageCount":1,"currentPage":1,"perPage":20}}
$I->seeResponseJsonMatchesJsonPath('$.items..title');
$I->seeResponseJsonMatchesXpath('//_meta//totalCount');
//delete
$I->wantTo('delete a news');
$id = $I->grabDataFromResponseByJsonPath('$.items..id');
$I->sendDELETE('/news/' . $id[0]);
$I->seeResponseCodeIs(HttpCode::NO_CONTENT);
//index
$I->wantTo('list empty news');
$I->sendGET('/news');
$I->seeResponseCodeIs(HttpCode::OK);
3)在验、運(yùn)行測試vendor/bin/codecept run -c blog
玷氏,
提示需要安裝flow/jsonpath
,執(zhí)行composer require -dev "flow/jsonpath"
腋舌。待安裝之后再次執(zhí)行測試即可盏触。
至此,REST的API測試完成侦厚。
Yii2下使用codeception進(jìn)行單元測試和API測試到此告一段落耻陕,后面可以繼續(xù)熟悉codeception的強(qiáng)大吧。
上一篇:準(zhǔn)備工作
下一篇:力爭上游