symfony 2
In my previous article, we demonstrated how to load sample data into our Symfony development environment.
在上一篇文章中 ,我们演示了如何将示例数据加载到我们的Symfony开发环境中。
The test data may not be useful as it stands on its own. When coupled with Functional Testing, however, it becomes a life saver.
测试数据本身可能没有用。 但是,与功能测试结合使用时,它可以节省生命。
功能测试 (Functional Testing)
Symfony’s official site has a useful document focusing on Unit Testing and Functional Testing. Be sure to take a look if you want to dig deeper into this topic.
Symfony的官方站点上有一个有用的文档,重点关注单元测试和功能测试。 如果您想更深入地研究该主题,请务必看一看。
I have covered Unit Tests to some extent in my previous article.
Unit Tests, in short, test the behavior of a class and/or its member functions. For example, I have created a Pagination class for my site to display all my book collections in pages, which also displays a list of pages for navigation purposes. To make the list of pages useful, the function must return an array of pages depending on the current page and the number of total pages so that the current page will be in the middle of the array. This is a built-in behavior of that function and does not relate to (but has to behave well when coupled with) different situations. That is where Unit Tests fit in.
简而言之,单元测试测试类和/或其成员函数的行为。 例如,我为我的网站创建了一个分页类,以在页面中显示我的所有藏书,还显示了用于导航目的的页面列表。 为了使页面列表有用,该函数必须根据当前页面和总页面数返回一个页面数组,以便当前页面位于数组的中间。 这是该函数的内置行为,与不同情况无关(但必须表现良好)。 那就是单元测试适合的地方。
If we look at testing the Pagination class in my earlier article, we can see very clearly how different scenarios are forged and the output tested:
如果我们在前一篇文章中测试Pagination类,则可以很清楚地看到如何伪造不同的场景以及如何测试输出:
$paginator=new Paginator(2, 101, 10);
$pages=$paginator->getTotalPages();
$this->assertEquals($pages, 11);
$list=$paginator->getPagesList();
$this->assertEquals($list, array(1,2,3,4,5));
$paginator=new Paginator(7, 101, 10);
$list=$paginator->getPagesList();
$this->assertEquals($list, array(5,6,7,8,9));
$paginator=new Paginator(10, 101, 10);
$list=$paginator->getPagesList();
$this->assertEquals($list, array(7,8,9,10,11));
Functional Testing is different. We don’t look at the “correctness” of a single function, which should be verified by a Unit Test, but look at the bigger picture. The question answered by Functional Testing is: Is our app performing well in the sense that it displays the right content, corresponds to a user’s interaction, etc?
功能测试是不同的。 我们不看单个功能的“正确性”,而应该通过单元测试来验证,而是看大图。 功能测试所回答的问题是:从显示正确内容,对应于用户交互等方面来说,我们的应用程序是否表现良好?
It can help us debug the app by repeating the user’s steps and reproducing the bug. If a user reports an error saying “When I do this, then that, with this data, the system is buggy”, we can simulate the operation sequence and find some deeply rooted errors.
它可以通过重复用户的步骤并重现错误来帮助我们调试应用程序。 如果用户报告一个错误,说“当我这样做时,那么使用此数据,系统就出错了”,我们可以模拟操作顺序并找到一些根深蒂固的错误。
Let’s see how to functionally test the site. Take a look at the site’s index page again. What should we test in this page’s content so that we are confident the site is performing as it should?
让我们看看如何对网站进行功能测试。 再次查看该网站的索引页面。 我们应该在该页面的内容中进行哪些测试,以便使我们确信该网站的运行情况是否应该达到预期?
Of course, static content is none of our business as far as testing goes. Any errors in the static content are considered typos and are not in the scope of a functional test. It is therefore clear we will focus on the “dynamic” content and user interactions.
当然,就测试而言,静态内容与我们无关。 静态内容中的任何错误均视为错别字,不在功能测试范围内。 因此很明显,我们将专注于“动态”内容和用户交互。
In this index page, we will focus on the following dynamic content based on our database’s sample data and app logic:
在此索引页面中,我们将基于数据库的样本数据和应用程序逻辑来关注以下动态内容:
We should have a total 101 books in our library.
我们的图书馆应该总共有101本书。
The latest book collected should have a purchase date of 1970-01-01 and its author is “Special“.
收集的最新书籍的购买日期应为1970-01-01 ,其作者为“ Special ”。
We should have 2 article collections (headlines) for my book readings and the latest review should be titled as “Review 2“.
我的读书应该有2篇文章集(标题),而最新的评论应该标题为“ 评论2 ”。
Your own app’s logic and data may be different, but it is critical to identify this important information in the page’s content. I am using the above 5 bolded figures so that I can be sure what I’m looking for: my controller is doing the correct counting on books and headlines and also selecting the latest book / headline from the database.
您自己的应用程序的逻辑和数据可能有所不同,但是在页面内容中识别此重要信息至关重要。 我使用上面的5个粗体数字,以便可以确定要查找的内容:控制器正在正确地计算书籍和标题,并从数据库中选择最新的书籍/标题。
The testing file to test the above conditions (or “assertions”) is located at: src/tr/rsywxBundle/Tests/Controller/DefaultControllerTest.php
and excerpted below:
测试上述条件(或“断言”)的测试文件位于: src/tr/rsywxBundle/Tests/Controller/DefaultControllerTest.php
,摘录如下:
<?php
namespace tr\rsywxBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DefaultControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET', '/');
// $text = $crawler->text();
// $fp = fopen('index.txt', 'w');
// fwrite($fp, $text);
// fclose($fp);
$this->assertTrue($crawler->filter('html:contains("1970-01-01")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("101 books")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("by Special")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("2 articles")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("Review 2")')->count() == 1);
//We click a link and go to the detail page
$links=$crawler->selectLink('Special Book Title')->links();
$link=$links[0];
$crawler=$client->click($link);
}
We create an HTTP client, and create a crawler that simulates user action. In this test case, we want to visit the home page of the site, so we use $crawler = $client->request('GET', '/');
.
我们创建一个HTTP客户端,并创建一个模拟用户操作的搜寻器。 在这个测试案例中,我们要访问网站的主页,因此我们使用$crawler = $client->request('GET', '/');
。
Then, to make sure the page content contains this key information identified earlier, we make several assertions using assertTrue
. For example, to translate “We should have total 101 books in our library” into:
然后,为确保页面内容包含先前标识的此关键信息,我们使用assertTrue
进行几个声明。 例如,将“我们的图书馆中应该总共有101本书”翻译成:
$this->assertTrue($crawler->filter('html:contains("101 books")')->count() == 1);
we are expecting that there should be exactly one occurrence of “101 books” in the response. I use a longer phrase “101 books” to avoid some other “101” from being counted. If this assertion fails, it can either mean that there are some typos in the template or that there is a logical flaw and the counting of all books fails.
我们希望回应中应该只出现一本“ 101本书”。 我使用较长的短语“ 101本书”来避免计算其他“ 101”。 如果此声明失败,则可能意味着模板中存在一些错别字,或者存在逻辑缺陷,并且所有账簿的计数都将失败。
To run the test, issue the following command in the terminal window:
要运行测试,请在终端窗口中发出以下命令:
php phuunit.phar -c app/
The “-c app/” argument simply tells PHPUnit to use the configuration found in the app
directory, which comes with the Symfony distribution.
“ -c app /”参数只是告诉PHPUnit使用在Symfony发行版随附的app
目录中找到的配置。
Voilà! All tests passed! Seeing this green bar feels great. If any assertions fail, the bar will be red.
瞧! 所有测试均通过! 看到这个绿色的酒吧感觉很棒。 如果任何断言失败,则该条将为红色。
Let’s test some other aspects, too. How about the links? Are they doing what they’re supposed to (in terms of URI and bringing us to the right page)?
让我们也测试其他方面。 链接如何? 他们是否正在按照自己的意愿去做(就URI而言,使我们进入正确的页面)?
For example, now that we’re sure the page is displaying the latest book collected, one would expect that if we click on that link, it will bring us to the book detail page showing more detailed information regarding that book.
例如,既然我们确定页面显示的是最新收集的图书,那么我们希望如果单击该链接,它将带我们进入显示该图书更详细信息的图书详细信息页面。
Let’s add a few more lines of code:
让我们再添加几行代码:
//We click a link and go to the detail page
$links=$crawler->selectLink('Special Book Title')->links();
$link=$links[0];
$crawler=$client->click($link);
$this->assertTrue($crawler->filter('html:contains("ISBN: 123456789")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("tag1")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("tag2")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("tag3")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("tag4")')->count() == 1);
$this->assertTrue($crawler->filter('html:contains("tag5")')->count() == 1);
In this code segment, we select the link with its text as “Special Book Title”. Based on our data fixture and app logic, there should be two of them in the index page. Either one shall bring us to the detail page of this book so I am using the first one:
在此代码段中,我们选择带有文本的链接作为“ Special Book Title”。 根据我们的数据工具和应用逻辑,索引页面中应该有两个。 任何人都可以将我们带到本书的详细页面,因此我正在使用第一本书:
By making the above additional assertions, we are sure we are viewing the book (the “special” book we created earlier) as identified by its ISBN and 5 tags. We are also sure that the link in the homepage correctly takes us to the book detail page showing the book listed in the homepage (as the latest collected book).
通过做出上述其他声明,我们确定我们正在查看由ISBN和5个标记标识的书(我们之前创建的“特殊”书)。 我们还确保主页中的链接正确地将我们带到显示该主页中列出的书籍(作为最新收集的书籍)的书籍详细信息页面。
In the testing code above, I have commented out a few lines. These lines, if uncommented, will write the page content into a text file. During the testing process, there may be some weird behavior (e.g. you can see there is a string “Special Book Title” but the assertion of the existence of that same string simply fails). If that should occur, it is recommended to dump the response into a text file and search to see if there are uncommon issues.
在上面的测试代码中,我注释了几行。 这些行(如果未注释)会将页面内容写入文本文件。 在测试过程中,可能会有一些奇怪的行为(例如,您可以看到有一个字符串“ Special Book Title”,但是断言该字符串的存在只是失败了)。 如果发生这种情况,建议将响应转储到文本文件中并进行搜索以查看是否存在不常见的问题。
Note: Symfony uses a special test_env
(other than dev
and prod
) to run tests. We can even configure that environment to use a different, but similarly structured database. It is recommended to use a separate environment for pure testing purposes but for the simplicity of this article, we have skipped this process.
注意: Symfony使用特殊的test_env
( dev
和prod
除外)来运行测试。 我们甚至可以配置该环境以使用其他但结构相似的数据库。 建议将单独的环境用于纯测试目的,但是为了本文的简单起见,我们跳过了此过程。
结论 (Conclusion)
In this article, we covered running functional tests using PHPUnit in a Symfony app. A functional test alone won’t be very meaningful as there isn’t enough data to populate our pages in a more meaningful manner. Data fixtures come in handy for automating the data loading process and more importantly, populating the data in a controlled way so that we can make further assertions on the expected output.
在本文中,我们介绍了在Symfony应用中使用PHPUnit运行功能测试。 因为没有足够的数据以一种更有意义的方式填充我们的页面,所以仅凭功能测试就没有什么意义。 数据固定装置可用于自动执行数据加载过程,更重要的是,可以受控的方式填充数据,以便我们可以对预期的输出进行进一步的声明。
Feel free to comment and we will be happy to cover this topic in greater detail if you are interested.
随时发表评论,如果您有兴趣,我们很乐意更详细地介绍该主题。
symfony 2