简体   繁体   English

Symfony2 Doctrine MongoDB 回滚

[英]Symfony2 Doctrine MongoDB rollback

I am trying to build a secure of set of tests with Symfony2, Doctrine and MongoDB.我正在尝试使用 Symfony2、Doctrine 和 MongoDB 构建一组安全的测试。

What I need to do is to load a lot of fixtures when a test begin, and unload them once it ends.我需要做的是在测试开始时加载很多 fixtures,并在测试结束时卸载它们。 I thought of doing it with a transaction , but... I couldn't find documentation on how to do it with Doctrine and Mongo!我想用事务来做,但是......我找不到关于如何用 Doctrine 和 Mongo 做的文档!

I found good documentation in the Doctrine docs regarding how to do transactions with the ORM, but not regarding the ODM.我在 Doctrine 文档中找到了关于如何与 ORM 进行交易的很好的文档,但没有关于 ODM 的文档。

So I took a look at the source code of the Connection.php class used by Doctrine-Mongo too and I haven't found the beginTransaction , commit and rollback methods that the dbal version uses.所以我也看了一下Doctrine-Mongo使用Connection.php class的源码,没有找到dbal版本使用的beginTransactioncommitrollback方法。

I was clueless, then I asked myself "Is it even possible to rollback in MongoDB?", and the answer if found in the MongoDB FAQ was:我一头雾水,然后我问自己“是否有可能在 MongoDB 回滚?”,如果在MongoDB FAQ中找到答案是:

MongoDB does not use traditional locking or complex transactions with rollback MongoDB 不使用传统的锁定或复杂的交易回滚

:( So I guess that's why there is no beginTransaction or whatsoever in the ODM... :( 所以我想这就是为什么 ODM 中没有beginTransaction或任何内容的原因......

But my problem remains: how can I implement a sort of rollback for my tests?但我的问题仍然存在:如何为我的测试实现某种回滚?

The only idea I got right now is to manually get all the ids of the Document I load and then remove them in the tearDown() .我现在唯一的想法是手动获取我加载的文档的所有 ID,然后在tearDown()中删除它们。 But, well... it kinda sucks, doesn't it?但是,好吧……有点糟糕,不是吗?

Other ideas??其他想法??

EDIT: After my first comment to this question, regarding the fact that I want to have the same DB in test and development, I thought: why don't use a separate test database, where the development database gets copied when the tests start, and that can be light-heartedly dropped?编辑:在我对这个问题发表第一条评论之后,关于我想在测试和开发中拥有相同的数据库这一事实,我想:为什么不使用单独的测试数据库,在测试开始时复制开发数据库,那可以轻松丢弃吗?

Could it be a better idea?这会是一个更好的主意吗? It actually looks easier and more secure to me.它实际上对我来说看起来更容易和更安全。 What do you guys think?你们有什么感想?

Thanks:)谢谢:)

I am not using two separate DBs for development and testing我没有使用两个单独的数据库进行开发和测试

That's the first thing to address - because without a testing db, running tests will affect your development db and vice versa which is a terrible idea.这是首先要解决的问题——因为没有测试数据库,运行测试将影响您的开发数据库,反之亦然,这是一个糟糕的主意。 You should be able to run tests in your production environment with absolute confidence that nothing you do in a test will affect your deployed site.您应该能够在您的生产环境中运行测试,绝对有信心您在测试中所做的任何事情都不会影响您部署的站点。

Setup a test connection设置测试连接

So, modify your parameters.yml to have something like this:因此,将您的 parameters.yml 修改为如下所示:

database.host: localhost
database.port: 27017
database.db:   myappname

database.test.host: localhost
database.test.port: 27017
database.test.db:   myappname-test

In addition, in your app/config/config_test.yml file override the default connnection so that anything you trigger as part of a test which requests the default document manager will receive a manager pointing at your test db:此外,在您的 app/config/config_test.yml 文件中覆盖默认连接,以便您作为测试的一部分触发的任何请求默认文档管理器的内容都将收到一个指向您的测试数据库的管理器:

doctrine_mongodb:
    document_managers:
        default:
            database: %database.test.db%

Prepare for tests with fixtures准备测试夹具

Then, what you want to do effectively is:那么,你想要有效地做的是:

  • truncate relevant collections截断相关 collections
  • load fixtures加载夹具

on your test db before each test.在每次测试之前在您的测试数据库上。

Here's an example abstract test class:这是一个示例抽象测试 class:

<?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);
    }
}

Use fixtures in your tests在测试中使用固定装置

With the previous abstract test class, you can then write tests which use your fixture data - or not - as appropriate.通过前面的抽象测试 class,您可以编写使用或不使用您的夹具数据的测试,视情况而定。 Below is a trivial example.下面是一个简单的例子。

<?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.
    }

Before each test, the fixtures you've defined will be loaded (truncating the collections they affect), giving a consistent db state on which to base your tests.在每次测试之前,您定义的固定装置将被加载(截断它们影响的 collections),提供一致的 db state 作为测试的基础。

Just drop your MongoDB database before each test and then load the fixtures you need.只需在每次测试前删除您的 MongoDB 数据库,然后加载您需要的设备。 This way each test will be fully isolated.这样每个测试都将完全隔离。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM