作为PHP开发人员,您可以使用测试驱动开发(TDD)技术通过编写测试来开发软件。 通常,TDD将开发的每个任务划分为各个单元。 然后编写测试以确保该单元的行为符合预期。
每个使用测试驱动开发的项目都重复执行三个简单步骤:
- 为您要添加的下一功能编写测试。
- 编写功能代码,直到测试通过。
- 重构新旧代码以使其结构良好。
继续循环执行这三个步骤,一次进行一次测试,以建立系统的功能。 测试将帮助您进行重构,从而使您可以随着时间的推移改进设计,并使一些设计问题更加明显。
包含较小单个组件的测试称为单元测试 。 尽管可以独立执行单元测试,但是如果在将某些组件与其他组件集成时对其进行测试 ,则您正在执行集成测试 。 第三种测试是测试存根 。 测试存根允许您测试代码,而不必真正调用数据库。
为什么TDD有效
如今,由于您可能使用现代的PHP IDE语法,因此反馈已不是什么大问题。 开发的重要方面之一是确保代码能够实现您期望的功能。 由于软件很复杂(不同的组件相互集成),因此我们所有的期望都难以实现。 尤其是在项目结束时,由于您的开发,项目将变得更加复杂,因此更加难以调试和测试。
TDD验证代码是否按照您的预期进行。 如果出现问题,则仅需重新检查几行代码。 错误很容易发现和解决。 在TDD中,测试的重点是行为,而不是实现。 TDD提供了经过测试,设计和编码的行之有效的代码。
PHPUnit和Laravel
PHPUnit是用于单元测试PHP的事实上的标准。 从本质上讲,这是一个用于编写测试并提供运行测试和分析结果所需的工具的框架。 PHPUnit从Kent Beck的SUnit继承其结构和功能。
有几种不同的断言可以帮助您测试应用程序中各种调用的结果。 有时,您必须更有创造力才能测试更复杂的功能,但是PHPUnit提供的断言涵盖了您要测试的大多数情况。 以下是一些您将在测试中发现的更常见的列表 :
- AssertTrue :检查输入以确认它等于true。
- AssertFalse :检查输入以确认它等于假值。
- AssertEquals :对照另一个输入检查结果是否匹配。
- AssertArrayHasKey() :如果数组没有键,则报告错误。
- AssertGreaterThan :检查结果是否大于一个值。
- AssertContains :检查输入是否包含某个值。
- AssertType :检查变量是否属于某种类型。
- AssertNull :检查变量是否为空。
- AssertFileExists :验证文件是否存在。
- AssertRegExp :对照正则表达式检查输入。
默认情况下,Laravel中安装了PHPUnit 4.0,您可以运行以下命令对其进行更新:
composer global require "phpunit/phpunit=5.0.*"
Laravel根目录中的phpunit.xml
文件使您可以进行一些配置。 在这种情况下,如果要覆盖默认配置,则可以编辑文件:
./tests/
app/
如您在上面的代码中所看到的,我已经添加了样本(本文中未使用)数据库配置。
什么是教义ORM?
Doctrine是一个ORM,它实现数据映射器模式,并允许您将应用程序的业务规则与数据库的持久层完全分开。 要设置Doctrine,有一个桥梁可以与Laravel 5的现有配置匹配。 要在Laravel项目中安装Doctrine 2,我们运行以下命令:
composer require laravel-doctrine/orm
与往常一样,应将包作为服务提供者添加到app/config.php
:
LaravelDoctrine\ORM\DoctrineServiceProvider::class,
别名也应配置:
'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class
最后,我们发布带有以下内容的软件包配置:
php artisan vendor:publish --tag="config"
如何测试教义库
首先,您应该了解fixtures 。 夹具用于将受控数据集加载到数据库中,这是我们测试所需的。 幸运的是,Doctrine 2具有一个可帮助您为Doctrine ORM编写夹具的库。
要在我们的Laravel应用中安装灯具包,我们需要运行以下命令:
composer require --dev doctrine/doctrine-fixtures-bundle
让我们在tests/Fixtures.php
创建夹具:
namespace Test;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\FixtureInterface;
use app\Entity\Post;
class Fixtures implements FixtureInterface
{
/**
* Load the Post fixtures
* @param ObjectManager $manager
* @return void
*/
public function load(ObjectManager $manager)
{
$Post = new Post(['title'=>'hello world','body'=>'this is body']);
$manager->persist($Post);
$manager->flush();
}
}
如您所见,您的灯具类应实现FixtureInterface
并应具有load(ObjectManager $manager)
方法。 Doctrine2固定装置是PHP类,您可以在其中创建对象并将其持久化到数据库中。 要在Laravel中自动加载灯具,我们需要在Laravel根目录中修改composer.json
:
...
"autoload-dev": {
"classmap": [
"tests/TestCase.php",
"tests/Fixtures.php" //added here
]
},
...
然后运行:
composer dump-autoload
让我们在测试目录DoctrineTest.php
创建测试文件。
namespace Test;
use App;
use App\Entity\Post;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\DataFixtures\Loader;
use App\Repository\PostRepo;
class doctrineTest extends TestCase
{
private $em;
private $repository;
private $loader;
public function setUp()
{
parent::setUp();
$this->em = App::make('Doctrine\ORM\EntityManagerInterface');
$this->repository = new PostRepo($this->em);
$this->executor = new ORMExecutor($this->em, new ORMPurger);
$this->loader = new Loader;
$this->loader->addFixture(new Fixtures);
}
/** @test */
public function post()
{
$purger = new ORMPurger();
$executor = new ORMExecutor($this->em, $purger);
$executor->execute($this->loader->getFixtures());
$user = $this->repository->PostOfTitle('hello world');
$this->em->clear();
$this->assertInstanceOf('App\Entity\Post', $user);
}
}
在setUp()
方法中,我实例化了ORMExecutor
和Loader。 我们还将加载刚刚实现的Fixtures
类。
不要忘记/** @test */
批注非常重要,如果没有此注释,phpunit将返回No tests found in class
错误中未No tests found in class
。
要在我们的项目根目录中开始测试,只需运行以下命令:
sudo phpunit
结果将是:
PHPUnit 4.6.6 by Sebastian Bergmann and contributors.
Configuration read from /var/www/html/laravel/phpunit.xml.
Time: 17.06 seconds, Memory: 16.00M
OK (1 test, 1 assertion)
如果要在灯具之间共享对象,则可以按名称轻松添加对该对象的引用,然后再引用该对象以形成关系。 这是一个例子:
namespace Test;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\FixtureInterface;
use app\Entity\Post;
class PostFixtures implements FixtureInterface
{
/**
* Load the User fixtures
*
* @param ObjectManager $manager
* @return void
*/
public function load(ObjectManager $manager)
{
$postOne = new Post(['title'=>'hello','body'=>'this is body']);
$postTwo = new Post(['title'=>'hello there ','body'=>'this is body two']);
$manager->persist($postOne);
$manager->persist($postTwo);
$manager->flush();
// store reference to admin role for User relation to Role
$this->addReference('new-post',$postOne);
}
}
和评论夹具:
namespace Test;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\FixtureInterface;
use app\Entity\Post;
class CommentFixtures implements FixtureInterface
{
/**
* Load the User fixtures
*
* @param ObjectManager $manager
* @return void
*/
public function load(ObjectManager $manager)
{
$comment = new Comment(['title'=>'hello','email'=>'alirezarahmani@live.com','text'=>'nice post']);
$comment->setPost($this->getReference('new-post')); // load the stored reference
$manager->persist($comment);
$manager->flush();
// store reference to new post for Comment relation to post
$this->addReference('new-post',$postOne);
}
}
使用getReference()
和setReference()
两种方法,可以在灯具之间共享对象。
如果灯具的订购对您很重要,则可以使用灯具中的getOrder
方法轻松订购它们,如下所示:
public function getOrder()
{
return 5; // number in which order to load fixtures
}
请注意,该顺序与Loader类相关。
关于固定装置的重要事情之一是它们解决依赖性问题的能力。 您唯一需要添加的是夹具中的方法,如下所示:
public function getDependencies()
{
return array('Test\CommentFixtures'); // fixture classes fixture is dependent on
}
结论
这只是使用Laravel 5和PHPUnit进行测试驱动开发的描述。 测试存储库时,不可避免地要访问数据库。 在这种情况下,Doctrine装置很重要。
翻译自: https://code.tutsplus.com/tutorials/test-driven-development-with-laravel-doctrine--cms-25563