我正在尝试用Symfony2、Doctrine和MongoDB构建一组安全的测试。
我需要做的是在测试开始时加载很多固定装置,并在测试结束后卸载它们。我曾想过用事务来做,但是。。。我找不到关于如何使用Doctrine和Mongo的文档!
我在Doctrine文档中发现了关于如何与ORM进行交易的良好文档,但没有关于ODM的文档。
因此,我也查看了Doctrine Mongo使用的Connection.php
类的源代码,但我还没有找到dbal版本使用的beginTransaction
、commit
和rollback
方法。
我一无所知,然后我问自己"是否有可能在MongoDB中回滚?",如果在MongoDB常见问题解答中找到答案,答案是:
MongoDB不使用传统的锁定或带有回滚的复杂事务
:(所以我想这就是为什么ODM中没有beginTransaction
或任何东西…
但我的问题仍然存在:如何为我的测试实现某种回滚
我现在唯一的想法是手动获取我加载的文档的所有id,然后在tearDown()
中删除它们。但是。。。这有点糟糕,不是吗?
其他想法??
编辑:在我对这个问题的第一次评论之后,关于我希望在测试和开发中使用相同的数据库这一事实,我想:为什么不使用一个单独的测试数据库,在测试开始时,开发数据库会被复制,并且可以轻松地删除它
这是个更好的主意吗?对我来说,这看起来更容易也更安全。你们觉得呢?
感谢:)
我没有使用两个独立的数据库来开发和测试
这是首先要解决的问题,因为如果没有测试数据库,运行测试将影响开发数据库,反之亦然,这是一个糟糕的想法。您应该能够在生产环境中运行测试,并且绝对相信您在测试中所做的任何事情都不会影响您部署的站点。
设置测试连接
所以,修改你的参数.yml,使其具有如下内容:
database.host: localhost
database.port: 27017
database.db: myappname
database.test.host: localhost
database.test.port: 27017
database.test.db: myappname-test
此外,在你的app/config/config_test.yml文件中覆盖默认连接,这样你在测试中触发的任何请求默认文档管理器的东西都会收到一个指向你的测试数据库的管理器:
doctrine_mongodb:
document_managers:
default:
database: %database.test.db%
准备使用固定装置进行测试
然后,你想有效地做的是:
- 截断相关集合
- 负载固定装置
在每次测试之前在测试数据库中。
下面是一个抽象测试类示例:
<?php
use Doctrine'Common'DataFixtures'Executor'MongoDBExecutor as Executor,
Doctrine'Common'DataFixtures'Purger'MongoDBPurger as Purger,
Doctrine'Common'DataFixtures'Loader,
Doctrine'Common'DataFixtures'ReferenceRepository,
Symfony'Bundle'FrameworkBundle'Test'WebTestCase,
Symfony'Bundle'FrameworkBundle'Console'Application;
abstract class AbstractTest extends WebTestCase
{
/**
* Array of fixtures to load.
*/
protected $fixtures = array();
/**
* Setup test environment
*/
public function setUp()
{
$kernel = static::createKernel(array('environment' => 'test', 'debug' => false));
$kernel->boot();
$this->container = $kernel->getContainer();
$this->dm = $this->container->get('doctrine.odm.mongodb.document_manager');
if ($this->fixtures) {
$this->loadFixtures($this->fixtures, false);
}
}
/**
* Load fixtures
*
* @param array $fixtures names of _fixtures to load
* @param boolean $append append data, or replace?
*/
protected function loadFixtures($fixtures = array(), $append = true)
{
$defaultFixtures = false;
$loader = new Loader();
$refRepo = new ReferenceRepository($this->dm);
foreach ((array) $fixtures as $name) {
$fixture = new $name();
$fixture->setReferenceRepository($refRepo);
$loader->addFixture($fixture);
}
$purger = new Purger();
$executor = new Executor($this->dm, $purger);
$executor->execute($loader->getFixtures(), $append);
}
}
在测试中使用固定装置
使用前面的抽象测试类,您可以编写适当地使用固定装置数据(或不使用)的测试。下面是一个琐碎的例子。
<?php
use Your'AbstractTest,
Your'Document'Foo;
class RandomTest extends AbstractTest
{
/**
* fixtures to load before each test
*/
protected $fixtures = array(
'APP'FooBundle'DataFixtures'MongoDB'TestFoos',
'APP'FooBundle'DataFixtures'MongoDB'TestBars'
);
...
/**
* Check it gets an ID (insert succeeded)
*
*/
public function testCreateDefaults()
{
$foo = new Foo();
$this->dm->persist($foo);
$this->dm->flush();
$this->assertNotNull($foo->getId());
$this->assertSame('default value', $foo->getSomeProperty());
// etc.
}
/**
* Check result of something with a given input
*
*/
public function testSomething()
{
$foo = $this->dm->getRepository(APPFooBundle:Foo)->findByName('Some fixture object');
$foo->doSomething();
$this->assertSame('modified value', $foo->getSomeProperty());
// etc.
}
在每次测试之前,都会加载您定义的fixture(截断它们影响的集合),从而提供一个一致的数据库状态作为测试的基础。
只需在每次测试前删除MongoDB数据库,然后加载所需的fixture。这样,每个测试都将被完全隔离。