TDD:如何测试搜索


TDD: How to test a search?

我的网站将有一个先进的搜索。人们可以去那里搜索一个实体(例如汽车)。我创建了一些测试,根据搜索参数检查结果的数量。我考虑应该写什么测试,然后写,然后把数据添加到测试数据库中。但问题来了。当我向数据库插入新值时,我的旧测试中断了。那是因为我正在检查记录的数量…

<?php defined('SYSPATH') or die('No direct access allowed!');
class Search_Test extends PHPUnit_Extensions_Database_TestCase
{
    /**
     * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
     */
    public function getConnection()
    {
        $pdo = new PDO('mysql:dbname=db_test;host=127.0.0.1', 'root', null);
        return $this->createDefaultDBConnection($pdo, 'db_test');
    }
    /**
     * @return PHPUnit_Extensions_Database_DataSet_IDataSet
     */
    public function getDataSet()
    {
        $fixture = realpath(dirname(__FILE__).'/../data/fixture.xml');
        return $this->createXMLDataSet($fixture);
    }
    public function numberOfResultsDataProvider()
    {
        return array(
            array(1, null, null, 1),
            array(2, null, null, 3),
            array(3, null, null, 0),
            array('abc', null, null, 5),
            array(null, 1996, 2003, 3),
            array(null, 1996, 1999, 2),
            array(null, 2002, 2003, 1),
            array(null, 1500, 1800, 0),
            array(null, 2003, 2003, 1),
            array(null, null, 2005, 4),
            array(null, 1996, null, 4),
            array(null, null, null, 4),
            array(null, 2003, 1996, 0),
            array(null, 'abc', 2003, 4),
            array(null, '1996', '1999', 2),
            array(2, 2003, 2005, 2),
            array(null, null, null, 4),
        );
    }
    /**
     * @dataProvider numberOfResultsDataProvider
     */
    public function testNumberOfResults($brandId, $startYear, 
        $endYear, $numberOfResults
    ) {
        $search = ORM::factory('search');
        $search->setBrand($brandId)
            ->setYearRange($startYear, $endYear);
        $results = $search->results();
        $this->assertEquals($results->count(), $numberOfResults);
    }
}
?>

正常吗?当我创建新测试时,我的旧测试是否应该中断?

我的测试应该局限于数据吗?

我的搜索有太多参数,它们将以相同的形式(视图)使用。我应该为每个参数创建测试,还是应该一起测试它们?我应该把它分成更多的测试课程吗?

谢谢。

当进行涉及数据库的单元测试时,测试应该提供它将要测试的实际数据库。它可能只是一个简单的数据库,只包含与测试相关的值,比如一行匹配而另一行不匹配。根据在特定时间点存在的实时数据库的快照构建一堆测试也是合理的。

实现这一点的一个特别方便的方法是为测试专门创建一个Sqlite3数据库,并设置您的应用程序用于测试。

如果可能的话,应该切断代码获取数据的方式与数据库本身之间的联系。您的单元测试不应该依赖于数据库,并且您偶然发现了一些原因:您必须跨测试维护您的数据,创建新测试会导致其他测试中断,等等。如果您能够以某种方式伪造数据访问点并严格地在内存中返回数据,那将是理想的情况。我不知道这在PHP中有多容易,但是围绕数据访问代码构建一个接口,并使用该接口的假/模拟实现,可以在很大程度上帮助实现这一目标。

总之:
不可能在活动数据库上执行此操作

您需要一个单独的数据库用于测试。这实际上可能是对数据库的模拟。

然后,在测试之前,您需要将数据库准备为固定状态您期望它是(例如,从fixture加载数据或导入sql文件)。

那么,当您计划对原始数据库状态进行任何修改时,最好的选择是start database transaction

事务启动后,您可以在数据库上工作。

完成测试后,只需回滚事务,并使数据库处于干净状态。

Sqlite(已经提到过)在大多数情况下都可以做得很好,但它可能与实时数据库不同。无论如何,拥有不同的数据库适配器将非常有帮助。

您的测试在创建新测试时应该不应该中断,并且您的测试应该不应该依赖于数据。需要数据库中特定数据的测试应该将该数据放入数据库中。如果它要求数据库中没有其他数据,则应该清除其他所有数据。

当然,这会使您的测试变慢——所以模拟数据访问层。

我会让每个测试方法创建自己的数据,然后研究,断言,然后销毁自己的样本数据。

我通常会有一个共享的addData()方法,然后是一个数据库清理方法。希望您能够以某种方式使添加的数据可识别,以便您的清理方法可以是通用的。在您的示例中,也许您可以让所有brandId以"test-"开头,然后您的查询将搜索并删除brandId喜欢"test-%"的所有记录。