PHPUnit的数据库扩展可提供更多的防弹功能

In a previous article I discussed how you can create more robust code that queries your relational database through automated testing. But, as you know, querying is just one part of the picture. There are still other database operations, namely create, update, and delete. Just like retrieval, they too should be tested.

上一篇文章中,我讨论了如何创建更健壮的代码来通过自动测试查询关系数据库。 但是,正如您所知,查询只是图片的一部分。 还有其他数据库操作,即创建,更新和删除。 就像检索一样,也应该对其进行测试。

I’ll be exploring the other features of PHPUnit’s Database Extensions in this article while building on your knowledge of database testing in PHP. Specifically, the aim is:

我将在您掌握PHP数据库测试知识的基础上,探索本文中PHPUnit数据库扩展的其他功能。 具体来说,目标是:

  1. Learn how to test the result of an insert operation

    了解如何测试插入操作的结果
  2. Use PHPUnit’s Database assertion API to test the operation

    使用PHPUnit的数据库断言API测试操作

I’ve written this article to be self-contained so that you won’t have to read my first tutorial beforehand, but if you are new to Test Driven Development, I highly recommend you read it as well as the introduction to PHPUnit by Michelle Saver.

我写这篇文章是自成一体的,因此您不必事先阅读我的第一个教程,但是如果您是测试驱动开发的新手,我强烈建议您阅读它以及Michelle的PHPUnit简介 。保护程序。

To follow along with this article, you’ll need PHPUnit and the PHPUnit Database extension installed. Companion code can be found on GitHub; feel free to clone the source and play with it.

要继续本文,您需要安装PHPUnit和PHPUnit数据库扩展。 伴侣代码可以在GitHub上找到; 随时克隆源代码并使用它。

配置 (Setting Up)

Like in my first article, we’ll pretend that we’re the back-end guru of a fictional web-based magazine. The magazine is composed of many sections, and each section has feature articles which are set by the editor of that section.

就像在我的第一篇文章中一样,我们会假装我们是虚构的网络杂志的后端专家。 该杂志由许多部分组成,每个部分都有特色文章,这些文章由该部分的编辑者设置。

The first articles app lacked an implementation for saving an article to the database and this is the functionality we are going to test. We’ll assume that there is already a web interface where our editors can write their articles and this will the call code that saves an article to the database. The only thing that is lacking is the implementation itself and, of course, the test. By making this assumption, we can isolate the behavior we want to test.

第一篇文章应用程序缺少将文章保存到数据库的实现,这是我们要测试的功能。 我们假定已经有一个Web界面,编辑人员可以在其中编写文章,这将是将文章保存到数据库的调用代码。 唯一缺少的是实现本身,当然还有测试。 通过做出此假设,我们可以隔离我们要测试的行为。

The first step is to use the database dump included in the source code to create the database in MySQL. Here’s a bird’s eye view:

第一步是使用源代码中包含的数据库转储在MySQL中创建数据库。 这是鸟瞰图:

alt

The next step, since we are writing a database test, is to set up the data. After all, we need something to test the operations on! Create an XML file named seed.xml, with the root element dataset and some sample data. For example, here’s how the file would look:

由于我们正在编写数据库测试,因此下一步就是设置数据。 毕竟,我们需要一些东西来测试操作! 用根元素dataset和一些样本数据创建一个名为seed.xml的XML文件。 例如,这是文件的外观:

<dataset>       
 <table name="sections">
  <column>id</column>
  <column>name</column>
  <row>
   <value>1</value>
   <value>PHP</value>
  </row>
 </table>
 <table name="articles">
  <column>id</columm>
  ...
 </table>
</dataset>

I recommend you to write your own data just for practice, although you can always use the sample dataset in the GitHub repo. All the tables there are filled already.

我建议您编写自己的数据只是为了练习,尽管您始终可以在GitHub存储库中使用示例数据集。 那里的所有表都已填满。

编写测试课 (Writing the Test Class)

Every time you run the test, you need to clear and insert the initial data into the database. This is done by overriding some methods in a class that extends PHPUnit_Extensions_Database_TestCase.

每次运行测试时,都需要清除并将初始数据插入数据库中。 这是通过重写扩展PHPUnit_Extensions_Database_TestCase的类中的某些方法来完成的。

<?php
class ArticleDaoTest extends PHPUnit_Extensions_Database_TestCase
{
    public function getConnection() {
        $pdo = new PDO("mysql:host=localhost;dbname=bulletproof",
            "root", "password");
        return $this->createDefaultDBConnection($pdo,
            "bulletproof");
    }

    public function getSetUpOperation() {
        // whether you want cascading truncates
        // set false if unsure
        $cascadeTruncates = false;

        return new PHPUnit_Extensions_Database_Operation_Composite(array(
            new PHPUnit_Extensions_Database_Operation_MySQL55Truncate($cascadeTruncates),
            PHPUnit_Extensions_Database_Operation_Factory::INSERT()
        ));
    }

    public function getDataSet() {
        return $this->createXMLDataSet("seed.xml");
    }
}

In the code above we overrode the getConnection() method to tell the test class to connect to the database and then get the test data from the return of getDataSet() method which we also overrode.

在上面的代码中,我们覆盖了getConnection()方法以告诉测试类连接到数据库,然后从我们也覆盖的getDataSet()方法的返回中获取测试数据。

Each test will truncate the tables and repopulate them using seed.xml to re-initialize the database. If you’re using MySQL 5.5 or above, you may encounter a problem, since the TRUNCATE command is not allowed for InnoDB tables that make use of foreign key constraints. To work around this restriction, we need to override its getSetUpOperation() method.

每个测试都将截断表,并使用seed.xml重新填充它们以重新初始化数据库。 如果您使用的是MySQL 5.5或更高版本,则可能会遇到问题,因为使用外键约束的InnoDB表不允许使用TRUNCATE命令。 要解决此限制,我们需要重写其getSetUpOperation()方法。

The PHPUnit_Extensions_Database_Operation_MySQL55Truncate class referenced in getSetUpOperation() is a custom subclass of PHPUnit_Extensions_Database_Operation_Truncate that overrides the execute() method:

getSetUpOperation()引用的PHPUnit_Extensions_Database_Operation_MySQL55Truncate类是PHPUnit_Extensions_Database_Operation_Truncate的自定义子类,它覆盖了execute()方法:

<?php
class PHPUnit_Extensions_Database_Operation_MySQL55Truncate extends PHPUnit_Extensions_Database_Operation_Truncate
{
    public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) {
        $connection->getConnection()->query("SET @PHAKE_PREV_foreign_key_checks = @@foreign_key_checks");
        $connection->getConnection()->query("SET foreign_key_checks = 0");
        parent::execute($connection, $dataSet);
        $connection->getConnection()->query("SET foreign_key_checks = @PHAKE_PREV_foreign_key_checks");
    }
}

As scary as it might look, the execute() method just remembers MySQL’s foreign keys setting, turns it off temporarily so the TRUNCATE command can proceed, and then restores the setting.

看上去很可怕, execute()方法仅记住MySQL的外键设置,暂时将其关闭,以便TRUNCATE命令可以继续进行,然后恢复该设置。

编写测试 (Writing Your Test)

Now it’s time to get our hands dirty writing the test itself. Suppose we agree on the following update for the IArticleDao interface:

现在是时候动手编写测试本身了。 假设我们同意IArticleDao接口的以下更新:

<?php
interface IArticleDao
{
    // here is the new method for saving
    public function save($article);

    // already implemented in the first article
    public function getArticles($sectionId);
}

The first test to write is the test for saving an article. It should ensure that the data of the article goes to the correct columns in the articles table.

编写的第一个测试是保存文章的测试。 它应确保商品数据进入articles表中的正确列。

<?php
class ArticleDaoTest extends PHPUnit_Extension_Database_TestCase
{
...
    public function testSaveArticle() {
        $article = new ArticleDAO();
        $article->save(array(
            "title" => "PHP is Great!",
            ...
            ));

        $resultingTable = $this->getConnection()
            ->createQueryTable("articles",
            "SELECT * FROM articles");
        
        $expectedTable = $this->createXmlDataSet(
            "expectedArticles.xml")
            ->getTable("articles");
        $this->assertTablesEqual($expectedTable,
            $resultingTable);   
    }
}

First we instantiate an instance of ArticleDAO and then call the new save() method we are testing and pass in the associative array representing the article. After we have saved the article, we want to check if the data has been saved correctly. We query for the contents of the table and place them in instance of a QueryTable.

首先,我们实例化ArticleDAO一个实例,然后调用我们正在测试的新save()方法,并传入表示该文章的关联数组。 保存文章后,我们要检查数据是否正确保存。 我们查询表的内容,并将其放在QueryTable实例中。

With the results of our save() operation in $resultingTable, we have to compare it with an expected data set. But where do we get this expected data set? From the method createXmlDataSet() and the XML file expectedArticles.xml.

根据$resultingTable save()操作的$resultingTable ,我们必须将其与预期数据集进行比较。 但是,我们从哪里得到这个预期的数据集呢? 从方法createXmlDataSet()和XML文件expectedArticles.xml

The getTable() method indicates we only want to get the contents of the articles table since this is the one we want to compare against our $resultingTable.

getTable()方法表明我们只想获取articles表的内容,因为这是我们要与$resultingTable进行比较的内容。

The final step would then be to compare the $resultingTable and $expectedTable which is done by feeding both variables to the assertTablesEqual() method.

然后,最后一步是比较$resultingTable$expectedTable ,这是通过将两个变量都提供给assertTablesEqual()方法来完成的。

The setup of the data set in expectedArticles.xml is the same as our seed.xml; here’s an example:

该数据集的设置expectedArticles.xml是一样的我们seed.xml ; 这是一个例子:

<dataset>
 <table name="articles">
  <column>id</column>
  <column>title</column>
...
  <row>
   <value>1</value>
   <value>PHP is Great!</value>
...
  </row>
 </table>
</dataset>

通过测试 (Passing the Tests)

In Test Driven Development, failure is the first step. If you run the test now it will fail because we haven’t written the implementation yet.

在测试驱动开发中,失败是第一步。 如果您现在运行测试,则它将失败,因为我们尚未编写实现。

jeune@Miakka:~/bulletproofing$ phpunit ArticleTest.php
PHPUnit 3.6.7 by Sebastian Bergman.

PHP Fatal error:  Class ArticleDAO contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (IArticleDAO::save) in /bulletproofing/ArticleDao.php on line 12

The next step is to write the code that will let us pass the test (that is, save an article to the database). Let’s implement the method that was added to our IArticleDAO interface.

下一步是编写使我们通过测试的代码(即,将文章保存到数据库)。 让我们实现添加到IArticleDAO接口的方法。

<php
class ArticleDAO implements IArticleDAO
{
...
    public function save($article) {
        $db = new PDO(
            "mysql:host=localhost;dbname=bulletproof", 
            "root", "password");

        $stmt = $db->prepare("INSERT INTO articles (title, description, content, preview_image, section_id) value (:title, :description, :content, :preview_image, :section_id)");
        $stmt->execute($article);                 
        return true;
    }
}

Now if we run things again, we should see a passing test.

现在,如果再次运行,我们应该会看到通过测试。

jeune@Miakka:~/Bulletproofing$ phpunit ArticleTest.php
PHPUnit 3.6.7 by Sebastian Bergman.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 1 assertion)

摘要 (Summary)

In this tutorial we’ve seen how to write a database test of an insert operation in PHP using PHPUnit’s Database Extension. The technique presented in this tutorial should also work for delete and update operations.

在本教程中,我们已经看到了如何使用PHPUnit的数据库扩展在PHP中编写插入操作的数据库测试。 本教程中介绍的技术也应适用于删除和更新操作。

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/more-bulletproofing-with-phpunit/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值