测试
每当你多写一行代码,你都在增加潜在的新bug。为了打造更好、更好可靠的程序,你需要对代码使用功能测试和单元测试。
PHPUnit测试框架 ¶
Symfony整合了一个独立类库 – 名为PHPUnit – 带给你丰富的测试框架。本章不覆盖PHPUnit本身,不过它有自己的 极佳文档。
推荐使用最新的稳定版PHPUnit,安装的是PHAR。
每一个测试 – 不管是单元测试(unit test)还是功能测试(functional test) - 都是一个PHP类,存放在你的Bundle的 Tests/
子目录下。如果你遵守这一原则,那么运行所有的程序级测试时,只需以下命令即可:
1 | $ phpunit |
PHPunit通过Symfony根目录下的 phpunit.xml.dist
文件进行配置。phpunit.xml.dist
文件进行配置。
代码覆盖(code coverage)可以通过 —coverage-*
选项来生成。要查看帮助信息,可使用 —help
来显示更多内容。
单元测试 ¶
单元测试是针对单一PHP类的测试,这个类也被称为“单元(unit)”。如果你需要测试你的应用程序层级的整体行为,参考[功能测试] (#catalog2)(Functional Tests)。
编写Symfony单元测试和编写标准的PHPUnit单元测试并无不同。假设,你有一个极其简单的类,类名是 Calculator
,位于app bundle的 Util/
目录下:
// src/AppBundle/Util/Calculator.phpnamespace AppBundle\Util; class Calculator{ public function add($a, $b) { return $a + $b; }}
为了测试它,创建一个 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); }}
根据约定, Tests/AppBundle
目录应该复制你的bundle下的单元测试文件所在的目录。因此,如果你要测试的类位于 src/AppBundle/Util
目录下,那么就把测试代码放在 tests/AppBundle/Util/
—coverage-*
选项来生成。要查看帮助信息,可使用 —help
来显示更多内容。单元测试 ¶
🎜单元测试是针对单一PHP类的测试,这个类也被称为“单元(unit)”。如果你需要测试你的应用程序层级的整体行为,参考[功能测试] (#catalog2)(Functional Tests)。🎜🎜编写Symfony单元测试和编写标准的PHPUnit单元测试并无不同。假设,你有一个极其简单的类,类名是Calculator
,位于app bundle的 Util/
目录下:🎜# run all tests of the application# 运行程序中的所有测试$ phpunit # run all tests in the Util directory# 运行指定目录下的所有测试$ phpunit tests/AppBundle/Util # run tests for the Calculator class# 仅运行Calculator类的测试$ phpunit tests/AppBundle/Util/CalculatorTest.php # run all tests for the entire Bundle# 运行整个bundle的测试$ phpunit tests/AppBundle/🎜为了测试它,创建一个
CalculatorTest
文件到你的bundle的 tests/AppBundle/Util
目录下:🎜
// 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() ); }}🎜
🎜
Tests/AppBundle
目录应该复制你的bundle下的单元测试文件所在的目录。因此,如果你要测试的类位于 src/AppBundle/Util
目录下,那么就把测试代码放在 tests/AppBundle/Util/
目录下。🎜🎜就像你的真实程序一样 – 自动加载被自动开启了,通过 bootstrap.php.cache
文件来完成(这部分的配置默认是在app/phpunit.xml.dist文件中)bootstrap.php.cache
文件来完成(这部分的配置默认是在app/phpunit.xml.dist文件中)
针对指定文件或目录的测试也很简单:
<?xml version="1.0" charset="utf-8" ?><phpunit> <php> <server name="KERNEL_DIR" value="/path/to/your/app/" /> </php> <!-- ... --></phpunit>
功能测试 ¶
功能测试(Functional tests)检查程序不同层面的整合情况(从路由到视图)。这些层面本身并不因PHPUnit的介入而发生改变,只不过它们有着独特的工作流:
制造一个请求(request);
测试响应(response);
点击一个链接,或提交一个表单;
测试响应;
清除然后重复。
你的第一个功能测试 ¶
功能测试是存放在 Test/AppBundle/Controller
目录下的普通PHP文件。如果要测试由你的 PostController
处理的页面,先创建一个新的PostControllerTest.php文件,并继承一个特殊的 WebTestCase
类。
作为例程,这个测试看起来可能像下面代码:
1
为了运行上面的功能测试, WebTestCase
类启动了程序内核。在多数情况下,这是自动完成的。但如果kernel位于非标准目录,你需要调整 phpunit.xml.dist
文件,重新设置 KERNEL_DIR
环境变量为你的kernel目录:
$crawler = $client->request('GET', '/post/hello-world');
createClient()
$link = $crawler ->filter('a:contains("Greet")') // 选择所有含有"Greet"文本之链接 ->eq(1) // 结果列表中的第二个 ->link() // 点击它; $crawler = $client->click($link);
| 测试响应(response);点击一个链接,或提交一个表单; | 测试响应;
你的第一个功能测试 ¶
🎜功能测试是存放在Test/AppBundle/Controller
目录下的普通PHP文件。如果要测试由你的 PostController
处理的页面,先创建一个新的PostControllerTest.php文件,并继承一个特殊的 WebTestCase
类。🎜🎜作为例程,这个测试看起来可能像下面代码:🎜$form = $crawler->selectButton('submit')->form(); // 设置一些值$form['name'] = 'Lucas';$form['form_name[subject]'] = 'Hey there!'; // 提交表单$crawler = $client->submit($form);🎜🎜🎜
WebTestCase
类启动了程序内核。在多数情况下,这是自动完成的。但如果kernel位于非标准目录,你需要调整 phpunit.xml.dist
文件,重新设置 KERNEL_DIR
环境变量为你的kernel目录:🎜🎜🎜🎜🎜// Assert that the response matches a given CSS selector.// 断言响应内容匹配到一个指定的CSS拾取器$this->assertGreaterThan(0, $crawler->filter('h1')->count());🎜
createClient()
方法返回了一个client,用它来模拟浏览器,使得你能抓取网站页面:🎜🎜🎜🎜🎜🎜🎜🎜$this->assertContains( 'Hello World', $client->getResponse()->getContent());🎜🎜🎜🎜
use Symfony\Component\HttpFoundation\Response; // ... // Assert that there is at least one h2 tag// with the class "subtitle"// 断言至少有一个h2标签,其class是subtitle$this->assertGreaterThan( 0, $crawler->filter('h2.subtitle')->count()); // Assert that there are exactly 4 h2 tags on the page// 断言页面有4个h2标签$this->assertCount(4, $crawler->filter('h2')); // Assert that the "Content-Type" header is "application/json"// 断言Content-Type头是application/json$this->assertTrue( $client->getResponse()->headers->contains( 'Content-Type', 'application/json' )); // Assert that the response content contains a string// 断言响应信息中包含一个字符串$this->assertContains('foo', $client->getResponse()->getContent());// ...or matches a regex$this->assertRegExp('/foo(bar)?/', $client->getResponse()->getContent()); // Assert that the response status code is 2xx// 断言响应状态码是2xx$this->assertTrue($client->getResponse()->isSuccessful());// Assert that the response status code is 404// 断言响应状态码是404$this->assertTrue($client->getResponse()->isNotFound());// Assert a specific 200 status code// 断言响应状态码是特殊的200$this->assertEquals( 200, // or Symfony\Component\HttpFoundation\Response::HTTP_OK $client->getResponse()->getStatusCode()); // Assert that the response is a redirect to /demo/contact// 断言响应是一个指向/demo/contact的跳转$this->assertTrue( $client->getResponse()->isRedirect('/demo/contact'));// ...or simply check that the response is a redirect to any URL// 或者仅言响应是一个跳转,不管指向的URL是什么$this->assertTrue($client->getResponse()->isRedirect());🎜🎜🎜🎜🎜
request()方法
(参考 更多请求方法request()方法
(参考 更多请求方法)返回了一个 Crawler
对象,用于从响应内容中选择元素(select elements),完成点击链接或提交表单这样的动作。
响应信息必须是XML或HTML文档格式,抓取器(Crawler)才会工作。若需要不带文档格式的纯响应内容(raw content),请使用 $client->getResponse()->getContent()
。
需要点击链接时,先用抓取器选择它,可使用XPath表达式或CSS拾取器来完成选 择,然后再使用client行为来点击它:
1
提交表单时非常相似:选取一个表单按钮,可选地覆写某些表单项的值,然后提交对应的表单:
$crawler = $client->request('GET', '/post/hello-world');
表单也可以处理上传,它还包含了用于填写不同类型的表单字段的方法(例如 select()
和 tick()
)返回了一个 Crawler
对象,用于从响应内容中选择元素(select elements),完成点击链接或提交表单这样的动作。
$client->getResponse()->getContent()
。request( $method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true)提交表单时非常相似:选取一个表单按钮,可选地覆写某些表单项的值,然后提交对应的表单:
$client->request( 'GET', '/post/hello-world', array(), array(), array( 'CONTENT_TYPE' => 'application/json', 'HTTP_REFERER' => '/foo/bar', 'HTTP_X-Requested-With' => 'XMLHttpRequest', ));
select()
和 tick()
)。有关细节请参考后面小节 有用的断言
为了让你尽快掌握测试,下面的例子列出了最常用且最有用的测试断言:
1
使用Test Client ¶ | 测试客户端(test client)模拟了一个HTTP客户端,就像一个浏览器,可以产生针对你Symfony程序的请求。 |
$client->insulate();🎜🎜🎜🎜
$client->back();$client->forward();$client->reload(); // Clears all cookies and the history// 清除所有cookie和历史记录$client->restart();🎜🎜🎜🎜🎜
request()
方法接受一个HTTP请求方式的参数和一个URL参数,返回一个Crawler实例。request()
方法接受一个HTTP请求方式的参数和一个URL参数,返回一个Crawler实例。
对于功能测试来说,写死request链接是最佳实践。如果在测试中生成URL时使用Symfony路由,它将无法侦测到针对此URL页面的任何改变,这可能导致用户体验受到冲击。
使用抓取器来找到响应内容中的DOM元素。这些元素可以被用在点击链接或提交表单等场合:
$container = $client->getContainer();$kernel = $client->getKernel();
此处的 click()
方法和 submit()
方法,都会返回一个 crawler
抓取器对象。这些方法是浏览你的程序页面的最佳方式,因为它们帮你做了很多事,像是从表单中侦测HTTP请求方式,或者给你提供一个很好的API用于文件上传。
在后面的 Crawler 小节中你将学习到更多关于链接Link和表单Form对象的知识。
request