TDD:如何测试搜索?
我的网站将有高级搜索。人们可以去那里搜索某个实体(例如汽车)。我创建了一些测试,根据搜索参数检查结果数量。我考虑应该编写什么测试,然后编写它,然后将数据添加到测试数据库中。但问题来了。当我将新值插入数据库时,我的旧测试失败了。那是因为我正在检查记录数...
<?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);
}
}
?>
正常吗?当我创建新测试时,我的旧测试是否应该中断?
我的测试应该受限于数据吗?
我的搜索有太多参数,它们将以相同的形式(视图)使用。我应该创建搜索每个参数的测试还是应该一起测试它们?我应该把它分成更多的测试类吗?
谢谢。
My website will have an advanced search. Pleople can go there and search about an entitiy (cars, for example). I've created some tests that check the number of results based on the search parameters. I think about what tests should I write, then i write it, then I add data to the test database. But here comes the issue. When I insert new values to the database my old tests break. That's because I'm checking the number of records...
<?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);
}
}
?>
Is it normal? Should my old tests break when I create new tests?
Should my tests be bounded to data?
My search has too many parameters and they will be used in the same form (view). Should I create tests searching for each parameter or should I test they together? Should I split it up in more test classes?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
当进行涉及数据库的单元测试时,测试应该提供将要测试的实际数据库。它可能只是一个简单的数据库,仅包含与测试相关的值,例如一行匹配,一行不匹配。针对实时数据库的快照构建一系列测试也是合理的,因为它存在于特定的时间点。
实现此目的的一种特别方便的方法是使用专门用于测试的 Sqlite3 数据库,并将您的应用程序设置为使用该数据库进行测试。
when doing a unit test that involves a database, the test should provide the actual database that it will test against. It might just be a simple database that contains only values that are relevant to the test, like one row that would match and one row that doesn't. It's also reasonable to build a bunch of tests against a snapshot of the live database as it existed at a particular point in time.
One particularly convenient way of achieving this is to us a Sqlite3 Database specifically for the test, and set your application to use that for the test.
如果可能的话,您应该切断代码获取数据的方式与数据库本身之间的链接。您的单元测试不应该依赖于数据库,并且您偶然发现了一些原因:您必须跨测试维护数据,创建新测试会导致其他测试中断,等等。如果您可以以某种方式伪造数据访问点并严格在内存中返回数据,那么这将是理想的情况。我不知道在 PHP 中这有多容易,但是围绕数据访问代码构建一个接口并使用该接口的假/模拟实现可以在帮助实现这一目标方面大有帮助。
If at all possible, you should sever the link between how your code is fetching your data and the database itself. Your unit tests should not depend on the database, and you've stumbled upon a few reasons why: You have to maintain your data across tests, creating new tests causes others to break, and so on. If you could somehow fake the data access point and return data strictly in-memory, that would be the ideal case. I don't know how easy it would be in PHP, but building an interface around your data access code and using a fake/mocked implementation of that interface could go a long way in helping to achieve this goal.
简而言之:
无法在实时数据库上执行此操作
您需要一个单独的数据库进行测试。这实际上可能是数据库的模拟。
然后,在测试之前,您需要准备数据库使其处于您期望的固定状态(例如,从固定装置加载数据或导入sql 文件)。
然后,当您计划对原始数据库状态进行任何修改时,最好的选择是启动数据库事务。
一旦事务开始,您就可以对数据库进行操作。
完成测试后,您只需回滚事务,并使数据库处于干净状态。
Sqlite(已经提到)在大多数情况下都可以正常工作,但它可能与实时数据库不同。不管怎样,拥有不同的数据库适配器将会非常有帮助。
In short:
No way of doing this on a live database
You need a separate database for tests. This may be a mock of database actually.
Then, before the tests, you need to prepare the database to be in the fixed state you expect it to be (e.g. load data from fixtures or import sql file).
Then, the best option is start database transaction when you plan to do any modification to the original database state.
Once the transaction is started, you may work on the database.
After you complete the tests, you just rollback the transaction, and have database in the clean state.
Sqlite (already mentioned) will do fine in most cases, but it may differ from the live database. Anyway, having different database adapters will be very helpful.
当您创建新测试时,您的测试不应该中断,并且您的测试不应该依赖于数据。需要数据库中特定数据的测试应将该数据放入数据库中。如果它要求数据库中没有其他数据,则应该清除其他所有数据。
当然,这会使您的测试变慢 - 因此请模拟数据访问层。
Your tests should not break when you create new tests, and your tests should not be data-dependent. A test that requires particular data in the database should place that data into the database. And if it requires that no other data be in the database, it should clear everything else out.
This, of course, will make your tests slow - so mock out the data access layer.
我会让每个测试方法创建自己的数据,然后进行搜索、断言,然后销毁自己的示例数据。
我通常会有一个共享的 addData() 方法,然后是一个数据库清理方法。希望您能够以某种方式使您添加的数据可识别,以便您的清理方法可以通用。在您的示例中,也许您可以让所有的brandId都以“test-”开头,然后您的查询将搜索并删除brandId类似“test-%”的所有记录。
I would have each test method create its own data, then persearch, assert, and then destroy its own sample data.
I'll typically have a shared addData() method, and then a database cleanup method. Hopefully you'll be able to make your added data identifiable in some way so that your cleanup method can be generic. In your example, maybe you could have all of your brandId's start with "test-", and then your query would search out and delete all records where the brandId like 'test-%'.