5

我正在尝试使用 Symfony2、Doctrine 和 MongoDB 构建一组安全的测试。

我需要做的是在测试开始时加载很多夹具,并在测试结束后卸载它们。我想用一个事务来做,但是......我找不到关于如何用 Doctrine 和 Mongo 做的文档!

我在 Doctrine 文档中找到了关于如何与 ORM 进行交易的很好的文档,但没有关于 ODM。

所以我也看了一下Doctrine-Mongo 使用的类的源代码,Connection.php我没有找到dbal 版本使用的beginTransaction,commitrollback方法。

我一无所知,然后我问自己“是否有可能在 MongoDB 中回滚?”,如果在MongoDB FAQ中找到答案是:

MongoDB 不使用具有回滚功能的传统锁定或复杂事务

beginTransaction:( 所以我想这就是为什么ODM中没有或没有任何东西......

但我的问题仍然存在:我怎样才能为我的测试实现某种回滚?

我现在唯一的想法是手动获取我加载的文档的所有 id,然后在tearDown(). 但是,嗯……这有点糟糕,不是吗?

其他想法??

编辑: 在我对这个问题的第一次评论之后,关于我希望在测试和开发中拥有相同的数据库这一事实,我想:为什么不使用单独的测试数据库,当测试开始时,开发数据库会被复制,可以轻松放弃吗?

这会是一个更好的主意吗?它实际上对我来说看起来更容易,更安全。你们有什么感想?

谢谢 :)

4

2 回答 2

4

我没有使用两个单独的数据库进行开发和测试

这是首先要解决的问题 - 因为没有测试数据库,运行测试会影响您的开发数据库,​​反之亦然,这是一个糟糕的主意。您应该能够绝对自信地在生产环境中运行测试,您在测试中所做的任何事情都不会影响您部署的站点。

设置测试连接

因此,修改您的 parameters.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.
    }

在每次测试之前,您定义的固定装置将被加载(截断它们影响的集合),提供一致的数据库状态作为测试的基础。

于 2012-04-04T14:30:46.237 回答
2

只需在每次测试之前删除您的 MongoDB 数据库,然后加载您需要的固定装置。这样,每个测试都将完全隔离。

于 2012-04-04T14:15:29.870 回答