【问题标题】:Independent functional tests with LiipFunctionalTestBundle and fixtures?使用 LiipFunctionalTestBundle 和夹具进行独立功能测试?
【发布时间】:2015-04-19 12:00:12
【问题描述】:

我正在向 Symfony2 项目添加测试。以前我在devtest 环境中使用相同的数据库,它使用的MySQL 数据库已经填充了与生产服务器上相同的数据。

这些测试是相互依赖的,因为一些测试依赖于之前的测试。例如,如果我有一个商店网站,我在购物车中添加了一个产品,然后从购物车中删除了该产品。所以我需要使用表单插入数据,然后才能删除它。

现在我想使用独立的功能测试,因为那是 recommended way (by one of Symfony2's developers)

我已正确配置 LiipFunctionalTestBundle 以在 test 环境中使用 SQLite 数据库,并且我已开始使用 DoctrineFixturesBundle 添加固定装置。

但我不知道每次功能测试需要加载多少数据。我应该在测试开始时加载什么夹具?当实体因为表之间的关系而依赖于其他实体时,如何处理CRUD操作?

假设我正在开发一家商店,我想要一些测试:

  1. 用户在其购物车中添加了一些产品
  2. 用户从购物车中删除了一件产品
  3. 用户订购剩余产品

我应该为每一步创建不同的夹具吗?这意味着我的固定装置需要以多种不同的状态存在:空购物车、订购了一种产品的购物车等。这对我来说似乎是正确的,但非常耗时,所以我想知道我的想法是否有效。

【问题讨论】:

    标签: symfony doctrine-orm phpunit functional-testing liipfunctionaltestbundle


    【解决方案1】:

    对于每个测试用例,为了隔离和性能,最好尽可能少加载夹具(测试套件可能会非常缓慢)。

    当fixture相互依赖时,您只需使用学说参考管理它们并相互链接,同时注意顺序。 例如,假设简单的用户和角色关系。

    管理角色夹具的通用类:

    abstract class BaseLoadRoleData extends AbstractFixture implements OrderedFixtureInterface
    {
    
    
        public function getOrder()
        {
            return 1;
        }
    
        protected function createRole(ObjectManager $manager, $rolename)
        {
            $role= new Role();
            $role->setName($rolename);
    
            $manager->persist($role);
            $manager->flush();
            $this->setReference('role-' . $rolename, $role);
        }
    }
    

    简单角色的专用类

    class LoadSimpleRoleData extends BaseLoadRoleData
    {
        public function load(ObjectManager $manager)
        {
            $this->createRole($manager, Role::SIMPLE);
        }
    }
    

    管理员角色的专用类

    class LoadAdminRoleData extends BaseLoadRoleData
    {
        public function load(ObjectManager $manager)
        {
            $this->createRole($manager, Role::ADMIN);
        }
    
    }
    

    还有用户: 用于管理用户夹具的通用类:

    abstract class BaseLoadUserData extends AbstractFixture implements OrderedFixtureInterface
    {
    
        /**
         * @var ContainerInterface
         */
        private $container;
    
        /**
         * {@inheritDoc}
         */
        public function setContainer(ContainerInterface $container = null)
        {
            $this->container = $container;
        }
    
        public function getOrder()
        {
            return 2;
        }
    
        protected function buildUser($username, $firstName = "",$lastName ="")
        {
            $user= new User();
            $user->setUsername($username);
            $user->setFirstName($firstName);
            $user->setLastName($lastName);
    
            return $user;
    
        }
    }
    

    简单用户的专用类

    class LoadSimpleUserData extends BaseLoadUserData {
    
        /**
         * Load data fixtures with the passed EntityManager
         *
         * @param Doctrine\Common\Persistence\ObjectManager $manager
         */
        function load(ObjectManager $manager)
        {
            $user = $this->buildUser($manager, "simple@example.com");
            $user->addRole($this->getReference('role-'.Role::SIMPLE));
            $manager->persist($user);
            $manager->flush();
            $this->setReference('user-' . "admin@example.com", $user);
    
        }
    }
    

    管理员用户的专用类

    class LoadAdminUserData extends BaseLoadUserData {
    
        /**
         * Load data fixtures with the passed EntityManager
         *
         * @param Doctrine\Common\Persistence\ObjectManager $manager
         */
        function load(ObjectManager $manager)
        {
            $user = $this->buildUser($manager, "admin@example.com");
            $user->addRole($this->getReference('role-'.Role::ADMIN));
            $manager->persist($user);
            $manager->flush();
            $this->setReference('user-' . "admin@example.com", $user);
    
        }
    

    现在您可以单独使用它,例如,基于 Liip 功能测试包:

    class LoginControllerTest {
    
        public function testAdminUserLogin()
        {
            $this->loadFixtures(array(
                'Acme\DemoBundle\DataFixtures\ORM\LoadAdminRoleData',
                'Acme\DemoBundle\DataFixtures\ORM\LoadAdminUserData'
            ));
    
            // you can now run your functional tests with a populated database
            $client = static::createClient();
            // ...
    
            // test the login with admin credential
        }
    
        public function testSimpleUserLogin()
        {
            // add all your fixtures classes that implement
            // Doctrine\Common\DataFixtures\FixtureInterface
            $this->loadFixtures(array(
                'Acme\DemoBundle\DataFixtures\ORM\LoadSimpleRoleData',
                'Acme\DemoBundle\DataFixtures\ORM\LoadSimpleUserData'
            ));
    
            // you can now run your functional tests with a populated database
            $client = static::createClient();
            // ...
    
            // test the login with simple user credential
    
        }
    
    }
    

    希望对您有所帮助。

    【讨论】:

    • 如果我还想测试具有johnbob 名字的用户怎么办?
    • 为john写一个专用的fixtures类并加载到专用的测试类中
    • 我应该复制并粘贴LoadSimpleUserData 类中的所有代码吗?有没有更好的方法?
    • 您当然可以使用创建用户等的通用方法创建层次结构,但只是用于测试目的的夹具类,显然总是必须管理项目演变的类(新的必填字段等在)。我只发布示例代码,但在我的项目中,我使用最小层次结构来创建通用测试用户并直接调用createTestUSer($name,$surname) 之类的方法
    • @A.L 我已经更新了我的答案,我不知道现在我更清楚我的意思了
    猜你喜欢
    • 2011-07-15
    • 2021-10-12
    • 2011-10-09
    • 1970-01-01
    • 1970-01-01
    • 2019-07-05
    • 2015-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多