duanbicheng3345 2012-04-04 14:06
浏览 68
已采纳

Symfony2 Doctrine MongoDB回滚

I am trying to build a secure of set of tests with Symfony2, Doctrine and MongoDB.

What I need to do is to load a lot of fixtures when a test begin, and unload them once it ends. I thought of doing it with a transaction, but... I couldn't find documentation on how to do it with Doctrine and Mongo!

I found good documentation in the Doctrine docs regarding how to do transactions with the ORM, but not regarding the 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, commitand rollback methods that the dbal version uses.

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 does not use traditional locking or complex transactions with rollback

:( So I guess that's why there is no beginTransaction or whatsoever in the ODM...

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(). 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 :)

  • 写回答

2条回答 默认 最新

  • dpzr52746 2012-04-04 14:30
    关注

    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:

    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:

    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
    • load fixtures

    on your test db before each test.

    Here's an example abstract test 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. 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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝
  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?
  • ¥15 让node服务器有自动加载文件的功能
  • ¥15 jmeter脚本回放有的是对的有的是错的