PHPUnit试验分离
我将Symfony 2.8(最新版本)用于一个web应用程序,其中可以单独使用/重用的应用程序的每个部分都是自己的捆绑包。例如,有一个NewsBundle、GalleryBundle、ContactBundle、AdminBundle(这是一个特例-它只是EasyAdminBundle收集特定bundle提供的特征的包装包)、UserBundle(存储用户实体和模板的FOSUserBundle的子bundle) 我的问题是,单元测试的最佳结构是什么 让我再解释一下:在我的UserBundle中,我想对我的FOSUserBundle实现进行测试。我有一个测试登录页面(通过HTTP状态代码)、登录失败(通过错误消息)、登录成功(通过特定代码元素)、记住我(通过Cookie)、注销(通过页面内容)的方法PHPUnit试验分离,php,unit-testing,symfony,Php,Unit Testing,Symfony,我将Symfony 2.8(最新版本)用于一个web应用程序,其中可以单独使用/重用的应用程序的每个部分都是自己的捆绑包。例如,有一个NewsBundle、GalleryBundle、ContactBundle、AdminBundle(这是一个特例-它只是EasyAdminBundle收集特定bundle提供的特征的包装包)、UserBundle(存储用户实体和模板的FOSUserBundle的子bundle) 我的问题是,单元测试的最佳结构是什么 让我再解释一下:在我的UserBundle中,
既然您的问题更具体了,我将提供一个带有一些解释的答案。你在第一次测试中所做的可能会起作用,但不是你应该测试的方式。与其说它是最佳实践,不如说它是您绕过单元测试的想法,对照单个工作单元检查假设。您的测试有几个“单元”的工作要测试,它们都应该在单独的测试中
以下是前两种情况下更合适测试的简明示例:
public function testLoginForm()
{
$client = self::createClient();
$crawler = $client->request('GET', '/admin/login');
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertEquals(1, $crawler->filter('form[action="/admin/login_check"]')->count());
$this->assertEquals(1, $crawler->filter('input[name="_username"]')->count());
$this->assertEquals(1, $crawler->filter('input[name="_password"]')->count());
$this->assertEquals(1, $crawler->filter('input[type="submit"]')->count());
}
public function testLoginFailure()
{
$client = self::createClient();
$crawler = $client->request('GET', '/admin/login');
$form = $crawler->selectButton('_submit')->form();
$form['_username'] = 'test';
$form['_password'] = '123';
$crawler = $client->submit($form);
$this->assertEquals(1, $crawler->filter('div[class="alert alert-error"]')->count());
}
这里有几件事
您担心代码重复和额外的代码行,但我刚刚创建了两个单独的测试,根本没有增加行数。我能够删除followRedirects()
调用,因为它不适用于那些测试,我通过简单地重新创建客户端和爬虫程序消除了两行克隆,这就不那么容易混淆了
对于您的代码,只有一个单元测试,但如果该测试失败,可能是由于多种不同的原因-登录失败、登录成功等。因此,如果该测试失败,您必须筛选错误消息并找出系统的哪个部分失败。通过分离测试,当测试失败时,您只需通过测试的名称就可以知道哪里出了问题
您可以通过分离测试来消除一些多余的代码注释://设置错误的用户数据
不再需要,因为测试本身被称为testLoginFailure()
它不仅是单元测试的最佳实践,而且在使用WebTestCase
时还有另一个警告,即您希望所有测试都被隔离。我试图创建一个整个类都可以使用的静态$client
变量,认为如果只实例化一个实例,可以节省内存/时间,但这会在开始运行多个测试时导致不可预测的行为。您希望您的测试单独进行
如果您确实试图消除冗余代码,还可以使用这些函数,在每个请求之前实例化$this->client
和$this->crawler
:
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Component\DomCrawler\Crawler;
/*
* @var Client
*/
private $client;
/*
* @var Crawler
*/
private $crawler;
/*
* {@inheritDoc}
*/
protected function setUp()
{
$this->client = self::createClient();
$this->crawler = $this->client->request('GET', '/admin/login');
}
/*
* {@inheritDoc}
*/
protected function tearDown()
{
unset($this->client);
unset($this->crawler);
}
…但随后您将创建类级代码来声明这些变量,实例化它们,并将它们分解。您还添加了很多额外的代码,这是您最初试图避免的。此外,您的整个测试类现在是僵化和不灵活的,因为您永远不能请求登录页面以外的页面。此外,PHPUnit本身声明:
测试用例对象的垃圾收集是不可预测的
如果您不记得手动清理您的测试,那么上面的声明是针对您的。因此,除了我上面描述的其他原因之外,您可能还会因为这些原因遇到意外行为
至于第二个问题,当然可以提供帮助函数或扩展现有的*TestCase
类。Symfony文档甚至为此提供了一个示例。您可以将其放在一个单独的测试类中,就像他们的文档一样,或者您可以创建自己的MyBaseTestCase
类,其中包含该函数
TL;DR如果重复使用大量相同的设置,请不要尝试巧妙地处理测试/测试用例,分离测试,并创建辅助函数或基本测试用例类进行扩展。为什么评级这么差?我该怎么做才能让我的问题变得更好呢?因为这个问题非常广泛,大部分是基于观点的,而且没有明确的答案。您可能需要一个更具体的问题,其中包含一些您尝试过的代码示例。我已经添加了当前的代码,并稍微更新了文本。您是对的,这可能是基于观点的,但我没有找到关于如何管理代码的“最佳实践”的内容。这将有助于我从社区体验中受益,因为这里有许多专业的开发人员。谢谢你的帮助!我担心我可以改进我的问题,以得到这样一个专业且有用的答案:)没问题-当您给出您尝试的代码示例时,审阅者更容易准确地了解您的意思并帮助您。
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Component\DomCrawler\Crawler;
/*
* @var Client
*/
private $client;
/*
* @var Crawler
*/
private $crawler;
/*
* {@inheritDoc}
*/
protected function setUp()
{
$this->client = self::createClient();
$this->crawler = $this->client->request('GET', '/admin/login');
}
/*
* {@inheritDoc}
*/
protected function tearDown()
{
unset($this->client);
unset($this->crawler);
}