測(cè)試
每當(dāng)你多寫一行代碼,你都在增加潛在的新bug。為了打造更好、更好可靠的程序,你需要對(duì)代碼使用功能測(cè)試和單元測(cè)試。
PHPUnit測(cè)試框架 ?
Symfony整合了一個(gè)獨(dú)立類庫 – 名為PHPUnit – 帶給你豐富的測(cè)試框架。本章不覆蓋PHPUnit本身,不過它有自己的 極佳文檔。
推薦使用最新的穩(wěn)定版PHPUnit,安裝的是PHAR。
每一個(gè)測(cè)試 – 不管是單元測(cè)試(unit test)還是功能測(cè)試(functional test) - 都是一個(gè)PHP類,存放在你的Bundle的 Tests/
子目錄下。如果你遵守這一原則,那么運(yùn)行所有的程序級(jí)測(cè)試時(shí),只需以下命令即可:
1 | $ phpunit |
PHPunit通過Symfony根目錄下的 phpunit.xml.dist
文件進(jìn)行配置。
代碼覆蓋(code coverage)可以通過 —coverage-*
選項(xiàng)來生成。要查看幫助信息,可使用 —help
來顯示更多內(nèi)容。
單元測(cè)試 ?
單元測(cè)試是針對(duì)單一PHP類的測(cè)試,這個(gè)類也被稱為“單元(unit)”。如果你需要測(cè)試你的應(yīng)用程序?qū)蛹?jí)的整體行為,參考[功能測(cè)試] (#catalog2)(Functional Tests)。
編寫Symfony單元測(cè)試和編寫標(biāo)準(zhǔn)的PHPUnit單元測(cè)試并無不同。假設(shè),你有一個(gè)極其簡(jiǎn)單的類,類名是 Calculator
,位于app bundle的 Util/
目錄下:
// src/AppBundle/Util/Calculator.phpnamespace AppBundle\Util; class Calculator{ public function add($a, $b) { return $a + $b; }}
為了測(cè)試它,創(chuàng)建一個(gè) CalculatorTest
文件到你的bundle的 tests/AppBundle/Util
目錄下:
// tests/AppBundle/Util/CalculatorTest.phpnamespace Tests\AppBundle\Util; use AppBundle\Util\Calculator; class CalculatorTest extends \PHPUnit_Framework_TestCase{ public function testAdd() { $calc = new Calculator(); $result = $calc->add(30, 12); // assert that your calculator added the numbers correctly! $this->assertEquals(42, $result); }}
根據(jù)約定, Tests/AppBundle
目錄應(yīng)該復(fù)制你的bundle下的單元測(cè)試文件所在的目錄。因此,如果你要測(cè)試的類位于 src/AppBundle/Util
目錄下,那么就把測(cè)試代碼放在 tests/AppBundle/Util/
目錄下。
就像你的真實(shí)程序一樣 – 自動(dòng)加載被自動(dòng)開啟了,通過 bootstrap.php.cache
文件來完成(這部分的配置默認(rèn)是在app/phpunit.xml.dist文件中)
針對(duì)指定文件或目錄的測(cè)試也很簡(jiǎn)單:
# run all tests of the application# 運(yùn)行程序中的所有測(cè)試$ phpunit # run all tests in the Util directory# 運(yùn)行指定目錄下的所有測(cè)試$ phpunit tests/AppBundle/Util # run tests for the Calculator class# 僅運(yùn)行Calculator類的測(cè)試$ phpunit tests/AppBundle/Util/CalculatorTest.php # run all tests for the entire Bundle# 運(yùn)行整個(gè)bundle的測(cè)試$ phpunit tests/AppBundle/
功能測(cè)試 ?
功能測(cè)試(Functional tests)檢查程序不同層面的整合情況(從路由到視圖)。這些層面本身并不因PHPUnit的介入而發(fā)生改變,只不過它們有著獨(dú)特的工作流:
制造一個(gè)請(qǐng)求(request);
測(cè)試響應(yīng)(response);
點(diǎn)擊一個(gè)鏈接,或提交一個(gè)表單;
測(cè)試響應(yīng);
清除然后重復(fù)。
你的第一個(gè)功能測(cè)試 ?
功能測(cè)試是存放在 Test/AppBundle/Controller
目錄下的普通PHP文件。如果要測(cè)試由你的 PostController
處理的頁面,先創(chuàng)建一個(gè)新的PostControllerTest.php文件,并繼承一個(gè)特殊的 WebTestCase
類。
作為例程,這個(gè)測(cè)試看起來可能像下面代碼:
// tests/AppBundle/Controller/PostControllerTest.phpnamespace Tests\AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class PostControllerTest extends WebTestCase{ public function testShowPost() { $client = static::createClient(); $crawler = $client->request('GET', '/post/hello-world'); $this->assertGreaterThan( 0, $crawler->filter('html:contains("Hello World")')->count() ); }}
為了運(yùn)行上面的功能測(cè)試, WebTestCase
類啟動(dòng)了程序內(nèi)核。在多數(shù)情況下,這是自動(dòng)完成的。但如果kernel位于非標(biāo)準(zhǔn)目錄,你需要調(diào)整 phpunit.xml.dist
文件,重新設(shè)置 KERNEL_DIR
環(huán)境變量為你的kernel目錄:
<?xml version="1.0" charset="utf-8" ?><phpunit> <php> <server name="KERNEL_DIR" value="/path/to/your/app/" /> </php> <!-- ... --></phpunit>
createClient()
方法返回了一個(gè)client,用它來模擬瀏覽器,使得你能抓取網(wǎng)站頁面:
1 | $crawler = $client->request('GET', '/post/hello-world'); |
request()方法
(參考 更多請(qǐng)求方法)返回了一個(gè) Crawler
對(duì)象,用于從響應(yīng)內(nèi)容中選擇元素(select elements),完成點(diǎn)擊鏈接或提交表單這樣的動(dòng)作。
響應(yīng)信息必須是XML或HTML文檔格式,抓取器(Crawler)才會(huì)工作。若需要不帶文檔格式的純響應(yīng)內(nèi)容(raw content),請(qǐng)使用 $client->getResponse()->getContent()
。
需要點(diǎn)擊鏈接時(shí),先用抓取器選擇它,可使用XPath表達(dá)式或CSS拾取器來完成選 擇,然后再使用client行為來點(diǎn)擊它:
$link = $crawler ->filter('a:contains("Greet")') // 選擇所有含有"Greet"文本之鏈接 ->eq(1) // 結(jié)果列表中的第二個(gè) ->link() // 點(diǎn)擊它; $crawler = $client->click($link);
提交表單時(shí)非常相似:選取一個(gè)表單按鈕,可選地覆寫某些表單項(xiàng)的值,然后提交對(duì)應(yīng)的表單:
$form = $crawler->selectButton('submit')->form(); // 設(shè)置一些值$form['name'] = 'Lucas';$form['form_name[subject]'] = 'Hey there!'; // 提交表單$crawler = $client->submit($form);
表單也可以處理上傳,它還包含了用于填寫不同類型的表單字段的方法(例如 select()
和 tick()
)。有關(guān)細(xì)節(jié)請(qǐng)參考后面小節(jié) 表單(Forms)。
現(xiàn)在你可以很容易地穿梭于程序中,使用斷言來測(cè)試頁面是否按你期待的那樣來執(zhí)行。使用抓取器(Crawler)可令斷言作用于DOM之上。
// Assert that the response matches a given CSS selector.// 斷言響應(yīng)內(nèi)容匹配到一個(gè)指定的CSS拾取器$this->assertGreaterThan(0, $crawler->filter('h1')->count());
或者直接測(cè)試響應(yīng)內(nèi)容,如果你只是想對(duì)內(nèi)容中是否包含某些文本來斷言的話,又或只需知道響應(yīng)內(nèi)容并非XML/HTML格式而已:
$this->assertContains( 'Hello World', $client->getResponse()->getContent());
使用Test Client ?
測(cè)試客戶端(test client)模擬了一個(gè)HTTP客戶端,就像一個(gè)瀏覽器,可以產(chǎn)生針對(duì)你Symfony程序的請(qǐng)求。
1 | $crawler = $client->request('GET', '/post/hello-world'); |
request()
方法接受一個(gè)HTTP請(qǐng)求方式的參數(shù)和一個(gè)URL參數(shù),返回一個(gè)Crawler實(shí)例。
對(duì)于功能測(cè)試來說,寫死request鏈接是最佳實(shí)踐。如果在測(cè)試中生成URL時(shí)使用Symfony路由,它將無法偵測(cè)到針對(duì)此URL頁面的任何改變,這可能導(dǎo)致用戶體驗(yàn)受到?jīng)_擊。
使用抓取器來找到響應(yīng)內(nèi)容中的DOM元素。這些元素可以被用在點(diǎn)擊鏈接或提交表單等場(chǎng)合:
$link = $crawler->selectLink('Go elsewhere...')->link();$crawler = $client->click($link); $form = $crawler->selectButton('validate')->form();$crawler = $client->submit($form, array('name' => 'Fabien'));
此處的 click()
方法和 submit()
方法,都會(huì)返回一個(gè) crawler
抓取器對(duì)象。這些方法是瀏覽你的程序頁面的最佳方式,因?yàn)樗鼈儙湍阕隽撕芏嗍?,像是從表單中偵測(cè)HTTP請(qǐng)求方式,或者給你提供一個(gè)很好的API用于文件上傳。
在后面的 Crawler 小節(jié)中你將學(xué)習(xí)到更多關(guān)于鏈接Link和表單Form對(duì)象的知識(shí)。
request
方法也可以被用于直接模擬表單提交,或是執(zhí)行更多復(fù)雜請(qǐng)求。一些有用的例子:
// Directly submit a form (but using the Crawler is easier!)// 直接提交表單(但是使用Crawler更容易些)$client->request('POST', '/submit', array('name' => 'Fabien')); // Submit a raw JSON string in the request body// 在請(qǐng)求過程中提交一個(gè)JSON原生串$client->request( 'POST', '/submit', array(), array(), array('CONTENT_TYPE' => 'application/json'), '{"name":"Fabien"}'); // Form submission with a file upload// 文件上傳表單的提交use Symfony\Component\HttpFoundation\File\UploadedFile; $photo = new UploadedFile( '/path/to/photo.jpg', 'photo.jpg', 'image/jpeg', 123);$client->request( 'POST', '/submit', array('name' => 'Fabien'), array('photo' => $photo)); // Perform a DELETE request and pass HTTP headers// 執(zhí)行一個(gè)DELETE請(qǐng)求并傳遞HTTP頭$client->request( 'DELETE', '/post/12', array(), array(), array('PHP_AUTH_USER' => 'username', 'PHP_AUTH_PW' => 'pa$$word'));
最后提醒一點(diǎn),你可以強(qiáng)制讓每次請(qǐng)求在它們各自的PHP進(jìn)程中被執(zhí)行,以避免當(dāng)同一腳本中有多個(gè)client時(shí)的副作用。
1 | $client->insulate(); |
瀏覽(browsing) ?
Client對(duì)象支持很多種真實(shí)瀏覽器中的操作:
$client->back();$client->forward();$client->reload(); // Clears all cookies and the history// 清除所有cookie和歷史記錄$client->restart();
使用內(nèi)部對(duì)象 ?
getInternalRequest() 和 getInternalResponse() 方法從Symfony 2.3起被引入。 如果你使用client來測(cè)試你的程序,你可能想要使用client內(nèi)部的一些對(duì)象:
$history = $client->getHistory();$cookieJar = $client->getCookieJar();
你還可以得到與最近一次請(qǐng)求相關(guān)的對(duì)象:
// the HttpKernel request instance// HttpKernel的請(qǐng)求實(shí)例$request = $client->getRequest(); // the BrowserKit request instance// 瀏覽器組件的請(qǐng)求實(shí)例$request = $client->getInternalRequest(); // the HttpKernel response instance// HttpKernel的響應(yīng)實(shí)例$response = $client->getResponse(); // the BrowserKit response instance// 瀏覽器組件的響應(yīng)實(shí)例$response = $client->getInternalResponse(); $crawler = $client->getCrawler();
如果你的請(qǐng)求沒有處在隔離運(yùn)行的狀態(tài)下,你還可以使用 Container
(容器)和 Kernel
(內(nèi)核):
$container = $client->getContainer();$kernel = $client->getKernel();
使用容器 ?
強(qiáng)烈推薦功能測(cè)試只測(cè)試響應(yīng)。但在特定的罕見情況下,你可能要使用內(nèi)部對(duì)象來書寫斷言。這時(shí),你可以調(diào)用Dependency Injection Container(依賴注入容器,即服務(wù)容器):
1 | $container = $client->getContainer(); |
注意,如果你是把client隔離運(yùn)行或者使用一個(gè)HTTP layer的話,上述功能無效。程序中的可用服務(wù)列表,你可通過 debug:container
命令行工具來查看。
在Symfony2.6之前這個(gè)命令是
container:debug
。
如果你需要檢查的信息在分析器(profiler)中可用,那么應(yīng)使用profiler來替代container。
使用分析數(shù)據(jù) ?
每一次請(qǐng)求,你都可以借助Symfony分析器(profiler)來收集關(guān)于該請(qǐng)求在內(nèi)部被處理的有關(guān)信息。例如,分析器常被用于驗(yàn)證指定頁面在加載過程中的數(shù)據(jù)庫查詢次數(shù)是否小于給定值。
為了得到最近一次請(qǐng)求的Profiler,按下例操作:
// enable the profiler for the very next request// 為接下來的請(qǐng)求開啟profiler$client->enableProfiler(); $crawler = $client->request('GET', '/profiler'); // get the profile 得到分析器$profile = $client->getProfile();
在測(cè)試中使用分析器的某些細(xì)節(jié),請(qǐng)參閱 如何在功能測(cè)試中使用分析器 一文。
重定向 ?
當(dāng)一個(gè)請(qǐng)求返回的是重定向響應(yīng)時(shí),client并不自動(dòng)跟進(jìn)。你可以檢測(cè)該響應(yīng)信息,然后通過 followRedirect()
方法來強(qiáng)迫跟進(jìn):
1 | $crawler = $client->followRedirect(); |
如果你需要client自動(dòng)跟進(jìn)所有重定向,可以強(qiáng)迫它實(shí)現(xiàn),使用 followRedirects()
方法:
1 | $client->followRedirects(); |
將 false
傳給 followRedirects()
方法,重定向?qū)⒉辉俦桓M(jìn):
1 | $client->followRedirects(false); |
抓取器(Crawler) ?
每當(dāng)你通過client制造一個(gè)請(qǐng)求時(shí)都有一個(gè)抓取器(Crawler)實(shí)例被返回。抓取器允許你遍歷HTML文檔,拾取DOM節(jié)點(diǎn),找到鏈接和表單。
遍歷(Traversing) ?
就像JQuery,抓取器有各種方法來遍歷HTML/XML文檔的DOM。例如,下述方法能找到所有 input[type=submit]
元素,并選擇頁面中的最后一個(gè),然后選擇它的臨近父元素:
$newCrawler = $crawler->filter('input[type=submit]') ->last() ->parents() ->first();
還有很多其他方法可以利用:
filter('h1.title')
- 匹配CSS拾取器的所有節(jié)點(diǎn)。
filterXpath('h1')
- 匹配XPath expression(表達(dá)式)的所有節(jié)點(diǎn)。
eq(1)
- 指定索引的節(jié)點(diǎn)。
first()
- 第一個(gè)節(jié)點(diǎn)。
last()
- 最后一個(gè)節(jié)點(diǎn)。
siblings()
- Siblings。
nextAll()
- 后續(xù)所有siblings。
previousAll()
- 之前所有siblings。
parents()
- 返回所有父節(jié)點(diǎn)。
children()
- 返回所有子節(jié)點(diǎn)。
reduce($lambda)
- 匿名函數(shù)$lambda不返回false時(shí)的所有節(jié)點(diǎn)。
由于上述每個(gè)方法都返回一個(gè) Crawler
抓取器對(duì)象,你可以通過對(duì)各方法的鏈?zhǔn)秸{(diào)用來減少節(jié)點(diǎn)拾取過程的代碼:
$crawler ->filter('h1') ->reduce(function ($node, $i) { if (!$node->getAttribute('class')) { return false; } }) ->first();
可以使用 count()
函數(shù)來得到存儲(chǔ)在一個(gè)Crawler中的節(jié)點(diǎn)數(shù)量: count($crawler)
提取信息 ?
抓取器可以從節(jié)點(diǎn)中提到信息:
// 返回第一個(gè)節(jié)點(diǎn)的屬性值$crawler->attr('class'); // 返回第一個(gè)節(jié)點(diǎn)的節(jié)點(diǎn)文本$crawler->text(); // Extracts an array of attributes for all nodes// (_text returns the node value)// returns an array for each element in crawler,// each with the value and href// 提取一個(gè)由所有節(jié)點(diǎn)的屬性組成的數(shù)組// (_text返回節(jié)點(diǎn)值)// 返回一個(gè)由抓取器中的每一個(gè)元素所組成的數(shù)組// 每個(gè)元素有value和href$info = $crawler->extract(array('_text', 'href')); // Executes a lambda for each node and return an array of results// 對(duì)每個(gè)節(jié)點(diǎn)執(zhí)行一次lambda函數(shù)(即匿名函數(shù)),最后返回結(jié)果數(shù)組$data = $crawler->each(function ($node, $i) { return $node->attr('href');});
鏈接 ?
為了選擇鏈接,你可以使用上述遍歷方法,或者使用更方便的 selectLink()
快捷方法:
1 | $crawler->selectLink('Click here'); |
如此即可選擇包含了給定文本的所有鏈接,或者是可以點(diǎn)擊的圖片而這些圖片的 alt
屬性中包含了給定文本。和其他的過濾方法類似,它也是返回一個(gè) Crawler
對(duì)象。
一旦你選中了一個(gè)鏈接(link),你就可以使用一個(gè)特殊的鏈接對(duì)象( link
object),它是一個(gè)專門用來處理鏈接的方法(類似 getMethod()
或 getUri()
這種,都屬于幫助方法/helpful method)。要點(diǎn)擊鏈接,使用Client的 click()
方法,并把鏈接對(duì)象傳進(jìn)去:
$link = $crawler->selectLink('Click here')->link(); $client->click($link);
表單 ?
表單可以通過它們的按鈕被選擇,而按鈕可以用selectButton()方法來選擇,正如選擇鏈接一樣:
1 | $buttonCrawlerNode = $crawler->selectButton('submit'); |
注意你選擇的是表單的按鈕而不是表單本身,因?yàn)楸韱慰梢杂卸鄠€(gè)按鈕。如果你使用遍歷API,記住必須要找到一個(gè)按鈕。
selectButton()
方法可以選中 button
(按鈕)標(biāo)簽,以及submit(提交)所在的標(biāo)簽(即 input
標(biāo)簽)。它使用按鈕的幾個(gè)部分來找到它們:
value
屬性值(The value attribute value)圖片按鈕的
id
屬性值或alt
屬性值(The id or alt attribute value for images)button
標(biāo)簽的id
屬性值或name
屬性值(The id or name attribute value for button tags)
當(dāng)你有了一個(gè)含有button的crawler之后,調(diào)用 form()
方法即可得到擁有這個(gè)button節(jié)點(diǎn)的form實(shí)例。
1 | $form = $buttonCrawlerNode->form(); |
當(dāng)調(diào)用 form()
方法時(shí),你還可以把一個(gè)裝有表單字段值的數(shù)組傳進(jìn)來,以替代默認(rèn)的:
$form = $buttonCrawlerNode->form(array( 'name' => 'Fabien', 'my_form[subject]' => 'Symfony rocks!',));
當(dāng)你要模擬一個(gè)特定的HTTP表單提交方式時(shí),把它傳到第二個(gè)參數(shù)中:
1 | $form = $buttonCrawlerNode->form(array(), 'DELETE'); |
Client負(fù)責(zé)提交表單實(shí)例:
1 | $client->submit($form); |
字段的值可以被傳到submit()方法的第二個(gè)參數(shù)中:
$client->submit($form, array( 'name' => 'Fabien', 'my_form[subject]' => 'Symfony rocks!',));
對(duì)于更復(fù)雜的情況,可以把表單實(shí)例作為數(shù)組,來分別設(shè)置每一個(gè)字段對(duì)應(yīng)的值:
// 改變某個(gè)字段的值/Change the value of a field$form['name'] = 'Fabien';$form['my_form[subject]'] = 'Symfony rocks!';
還有一個(gè)很好用的API,可根據(jù)字段類型來管理它們的值:
// 選取一個(gè)option單選或radio單選$form['country']->select('France'); // 選取一個(gè)checkbox$form['like_symfony']->tick(); // 上傳一個(gè)文件$form['photo']->upload('/path/to/lucas.jpg');
如果你有目的地想要選擇“無效的”select/radio值,參考 選擇無效的Choice Values。
你可以得到即將被提交的表單字段值,調(diào)用Form對(duì)象的 getValues()
方法即可。已上傳的文件也可以從一個(gè)分離出來的數(shù)組得到,這個(gè)數(shù)組由 getFiles()
方法返回。 getPhpValues()
以及 getPhpFiles()
方法同樣可以返回已經(jīng)提交的字段,只不過是PHP格式的(它把帶有方括號(hào)的鍵 – 比如 my_form[subject]
– 給轉(zhuǎn)換成PHP數(shù)組)。
添加或刪除表單到Collection ?
如果你使用了 Collection of Forms(表單集合),你不能對(duì)既有表單添加一個(gè) $form['task[tags][0][name]'] = 'foo
字段。這會(huì)導(dǎo)致一個(gè) Unreachable field "…"
錯(cuò)誤。因?yàn)?$form
只能被用于設(shè)置現(xiàn)有的字段。為了添加一個(gè)新字段,你不得不把值添加到一個(gè)原始數(shù)組中:
// Get the form. 取得表單$form = $crawler->filter('button')->form(); // Get the raw values. 取得原始數(shù)值$values = $form->getPhpValues(); // Add fields to the raw values. 給原始數(shù)組添加新字段$values['task']['tag'][0]['name'] = 'foo';$values['task']['tag'][1]['name'] = 'bar'; // Submit the form with the existing and new values.// 提交表單,包括既有值,以及新值$crawler = $this->client->request($form->getMethod(), $form->getUri(), $values, $form->getPhpFiles()); // The 2 tags have been added to the collection.// 2個(gè)新標(biāo)簽被添加到collection中$this->assertEquals(2, $crawler->filter('ul.tags > li')->count());
這里的 task[tags][0][name]
就是由JavaScript創(chuàng)建的字段的名字。
你可以移除一個(gè)既有字段,比如一個(gè)標(biāo)簽(tag):
// Get the values of the form. 取得表單數(shù)據(jù)$values = $form->getPhpValues(); // Remove the first tag. 移除第一個(gè)標(biāo)簽unset($values['task']['tags'][0]); // Submit the data. 提交數(shù)據(jù)$crawler = $client->request($form->getMethod(), $form->getUri(), $values, $form->getPhpFiles()); // The tag has been removed. 標(biāo)簽已被移除$this->assertEquals(0, $crawler->filter('ul.tags > li')->count());
配置測(cè)試 ?
功能測(cè)試所用到的Client創(chuàng)建了一個(gè)Kernel,用于在一個(gè)特殊的 test
測(cè)試環(huán)境中運(yùn)行。由于Symfony在測(cè)試時(shí)加載的是 app/config/config_test.yml
,你可以調(diào)整任何一項(xiàng)用于測(cè)試的“程序級(jí)”設(shè)置。
例如,默認(rèn)的Swift Mailer被設(shè)置為不要在測(cè)試環(huán)境發(fā)送郵件。配置文件中的相關(guān)選項(xiàng)是這樣設(shè)置的:
PHP:// app/config/config_test.php // ...$container->loadFromExtension('swiftmailer', array( 'disable_delivery' => true,));
XML:<!-- app/config/config_test.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:swiftmailer="http://symfony.com/schema/dic/swiftmailer" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmailer-1.0.xsd"> <!-- ... --> <swiftmailer:config disable-delivery="true" /></container>
YAML:# app/config/config_test.yml # ...swiftmailer: disable_delivery: true
你也可以使用一個(gè)完全不同的環(huán)境,或者覆寫掉默認(rèn)的debug模式( true
),通過把相關(guān)選項(xiàng)傳給 createClient()
方法即可:
$client = static::createClient(array( 'environment' => 'my_test_env', 'debug' => false,));
如果你的程序需要通過一些HTTP頭才能運(yùn)轉(zhuǎn),把它們傳給createClient()方法的第二個(gè)參數(shù):
$client = static::createClient(array(), array( 'HTTP_HOST' => 'en.example.com', 'HTTP_USER_AGENT' => 'MySuperBrowser/1.0',));
你也可以在每一個(gè)request基本過程中覆寫HTTP header:
$client->request('GET', '/', array(), array(), array( 'HTTP_HOST' => 'en.example.com', 'HTTP_USER_AGENT' => 'MySuperBrowser/1.0',));
client在 test
測(cè)試環(huán)境下是容器中的可用服務(wù)(或者無論什么環(huán)境,只要 framework.test選項(xiàng)被開啟)。這意味著只要需要,你可以覆寫整個(gè)client服務(wù)。
PHPUnit配置 ?
每一個(gè)程序,有它自己的PHPUnit配置,就存在 app/phpunit.xml.dist
文件中。你可以編輯這個(gè)文件,改變默認(rèn)值,或創(chuàng)建一個(gè)僅供你本地機(jī)器設(shè)定測(cè)試選項(xiàng)的 app/phpunit.xml
文件。
將 app/phpunit.xml.dist
文件存在你的代碼寶庫中并且忽略 app/phpunit.xml
文件。
默認(rèn)是只有存放在標(biāo)準(zhǔn)目錄(src//Bundle/Tests, src//Bundle/Bundle/Tests, src/*Bundle/Tests)下面你的自定義bundle中的測(cè)試才能夠被phpunit命令執(zhí)行。這在app/phpunit.xml.dist文件中已經(jīng)配置好了:
<!-- app/phpunit.xml.dist --><phpunit> <!-- ... --> <testsuites> <testsuite name="Project Test Suite"> <directory>../src/*/*Bundle/Tests</directory> <directory>../src/*/Bundle/*Bundle/Tests</directory> <directory>../src/*Bundle/Tests</directory> </testsuite> </testsuites> <!-- ... --></phpunit>
但是你也可以很容易地添加更多目錄。例如,下列配置添加了自定義目錄lib/tests中的測(cè)試:
<!-- app/phpunit.xml.dist --><phpunit> <!-- ... --> <testsuites> <testsuite name="Project Test Suite"> <!-- ... - - -> <directory>../lib/tests</directory> </testsuite> </testsuites> <!-- ... - - -></phpunit>
為了在代碼覆蓋中包容其他目錄,還需添加
<!-- app/phpunit.xml.dist --><phpunit> <!-- ... --> <filter> <whitelist> <!-- ... --> <directory>../lib</directory> <exclude> <!-- ... --> <directory>../lib/tests</directory> </exclude> </whitelist> </filter> <!-- ... - - -></phpunit>