单元测试 – Symfony2,固定装置和单元测试.在测试期间在ManyToMany Relation中添加测试数据

我目前面临一个大问题.在单元测试期间,我在测试开始时使用fixture来将数据添加到数据库中(一切正常),我想使用与fixture相同的代码,在测试期间添加数据.例如:

实体人:

class Person {
 
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    [...]
 
    /**
     * @ORM\ManyToMany(targetEntity="Family", cascade={"persist"})
     * @Assert\Count(min = "1")
     */
    private $families;
 
    public function __construct() {
        $this->families = new \Doctrine\Common\Collections\ArrayCollection();
    }
 
    public function addFamily(Family $family) {
        $this->families[] = $family;
        return $this;
    }
 
    [...]
 
}

实体家庭:

class Family {
 
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    /**
     * @var string
     * @Gedmo\Slug(fields={"name"})
     * @ORM\Column(name="slug", type="string", length=255, unique=true)
     */
    private $slug;
 
    [...]
 
}

固定装置:

class Fixture1 implements FixtureInterface, ContainerAwareInterface {
    /**
     * @var Family[]
     */
    public $families = array();
 
    public function setContainer(ContainerInterface $container = null) {
        $this->container = $container;
    }
 
    public function load(ObjectManager $manager) {
        $this->manager = $manager;
        $SmithFamily= $this->addFamily("Smith");
        $this->addPerson("Will", "Smith", array($SmithFamily));
    }
 
    public function addFamily($name) {
        $family = new Family();
        $family->setName($name);
        $this->manager->persist($family);
        $this->manager->flush();
        $this->families[$name] = $family;
        return $family;
    }
     
    public function addPerson($firstName, $lastName, array $families = array()) {
        $person = new Person();
        $person->setFirstname($firstName);
        $person->setLastname($lastName);
        foreach ($families as $family) {
            $person->addFamily($family);
        }
         
        $this->manager->persist($person);
        $this->manager->flush();
        return $person;
    }
}

单元测试 :

class PersonTest extends WebTestCase {
 
    public $fixtures;
 
    protected function setUp() {
        $client = self::createClient();
        $container = $client->getKernel()->getContainer();
        $em = $container->get('doctrine')->getManager();
  
        // Purge tables
        $purger = new \Doctrine\Common\DataFixtures\Purger\ORMPurger($em);
        $executor = new \Doctrine\Common\DataFixtures\Executor\ORMExecutor($em, $purger);
        $executor->purge();
  
        // Load fixtures
        $loader = new \Doctrine\Common\DataFixtures\Loader;
        $this->fixtures = new Fixture1();
        $this->fixtures->setContainer($container);
        $loader->addFixture($this->fixtures);
        $executor->execute($loader->getFixtures());
         
        parent::setUp();
    }
 
    public function test1() {
        $this->fixtures->addPerson("James", "Smith", array($this->fixtures->families['Smith']));
        [...]
    }
 
}

当我执行测试时,我得到了这个错误:

Doctrine\DBAL\Exception\UniqueConstraintViolationException: An
exception occurred while executing ‘INSERT INTO family (slug, name,
created, last_updated_date, deleteddate) VALUES (?, ?, ?, ?, ?)’ with
params [“smith”, “Smith”, “2015-01-10 13:59:28”, “2015-01-10
13:59:28”, null]: SQLSTATE[23000]: Integrity constraint violation:
1062 Duplicata du champ ‘smith’ pour la clef ‘UNIQ_A5E6215B989D9B62’

此错误表示“slug”字段必须是唯一的.它试图添加一个新的家庭,尽管家庭已经存在.它应该只是增加新人(詹姆斯史密斯)和家庭之间的关系.我不明白为什么!
任何的想法 ?

最佳答案
让我一步一步来看,接下来比你要求的要多一些,但我希望它会帮助你写清洁测试,因为我在工作场所看到了很多不好的测试:

首先,这不是单元测试,只是你知道.它被称为功能测试,因为您正在测试整个系统功能,而不是一个小型的单元.

其次,不要仅仅为了获取容器而创建新客户端(createClient调用).通常情况下,每个测试用例不需要多个客户端.如果你真的想在setUp方法中创建它,那么将它存储在TestCase字段中(在你将用于它的TestCase类中创建一个受保护的字段)并将其存储在setUp方法中,这样你就可以使用它作为测试用例.这是必要的,因为在某些时候,您将通过实例化多个容器(每次调用它时createClient实例化一个新容器)并且例如具有多个EntitiManagers来在您的测试套件中引入错误.然后在某些时候你会有一些实体属于一个EntityManager而另一些实体属于另一个(这是可以管理的,但如果你不知道Doctrine是如何工作的,最好不要面对它).

第三,你根本不需要夹具加载器.此外,您不需要在此处调用purge,因为它是从执行程序自动调用的.这就是setUp中的代码如下所示:

$this->client = self::createClient();
$em = $this->getKernel()->getContainer()->get("doctrine.orm.default_entity_manager");
$executor = new ORMExecutor($em, new ORMPurger());
$executor->execute([new Fixture1()]);

parent::setUp();

我觉得这样简单得多.

此外,我不确定为什么你需要你的夹具成为ContainerAware,因为你不能尽可能地使用容器,而且在大多数情况下你实际上并不需要.

第四,永远不要将EntityManager存储在Fixture实例中.它应该通过加载方法获得它,使用它来加载所有内容并完全忘记它.

尝试更改您的fixture方法以期望manager作为参数,而不是将其用作字段和调用(new Fixture1) – > addPerson($this-> client-> getKernel() – > getContainer() – &gt ; get(“doctrine.orm.default_entity_manager”),“James”,“Smith”,$existingFamily);从你的测试方法.看起来你可以在某个时候使用不同的管理器,所以第二个不知道关于家庭的问题,当你为Person中的家族设置cascade = {persist}时,它会自动保留它并尝试重新插入.

转载注明原文:单元测试 – Symfony2,固定装置和单元测试.在测试期间在ManyToMany Relation中添加测试数据 - 代码日志