Gui*_*hem 5 unit-testing symfony
我目前面临一个大问题.在单元测试期间,我在测试开始时使用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;
}
[...]
}
Run Code Online (Sandbox Code Playgroud)
实体家庭:
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;
[...]
}
Run Code Online (Sandbox Code Playgroud)
固定装置:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
单元测试 :
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']));
[...]
}
}
Run Code Online (Sandbox Code Playgroud)
当我执行测试时,我得到了这个错误:
Doctrine\DBAL\Exception\UniqueConstraintViolationException:使用params ["smith","Smith"执行'INSERT INTO family(slug,name,created,last_updated_date,deleteddate)VALUES(?,?,?,?,?)'时发生异常","2015-01-10 13:59:28","2015-01-10 13:59:28",null]:SQLSTATE [23000]:完整性约束违规:1062 Duplicata du champ'smith'pour la clef 'UNIQ_A5E6215B989D9B62'
此错误表示"slug"字段必须是唯一的.它试图添加一个新的家庭,尽管家庭已经存在.它应该只是增加新人(詹姆斯史密斯)和家庭之间的关系.我不明白为什么!任何的想法 ?
让我一步一步来,下面的内容比您要求的要多一些,但我希望它能帮助您编写更干净的测试,因为我在工作场所已经看到了足够多的糟糕测试:
首先,这不是单元测试,只是让你知道。它被称为功能测试,因为您正在测试整个系统功能,而不是一个类的小单元。
其次,不要仅仅为了获取容器而创建新客户端(createClient 调用)。更重要的是,通常每个测试用例不需要多个客户端。如果您确实想在 setUp 方法中创建它,则将其存储在 TestCase 字段中(在您将使用的 TestCase 类中创建一个受保护的字段)并将其存储在 setUp 方法中,以便您可以在测试用例中使用它。这是必要的,因为在某些时候,您将通过实例化多个容器(createClient 每次调用它时实例化一个新容器)并拥有多个 EntitiManagers 来在测试套件中引入错误。然后在某个时刻,您将拥有一些属于一个 EntityManager 的实体,而其他实体属于另一个 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();
Run Code Online (Sandbox Code Playgroud)
我认为这样就简单多了。
另外,我不确定为什么你需要你的装置是 ContainerAware,因为据我所知,你不使用容器,而且实际上在大多数情况下你不需要这样做。
第四,切勿将 EntityManager 存储在 Fixture 实例中。它应该通过load方法来获取它,使用它来加载所有内容并完全忘记它。
尝试更改您的固定方法以期望 manager 作为参数,而不是将其用作字段并(new Fixture1)->addPerson($this->client->getKernel()->getContainer()->get("doctrine.orm.default_entity_manager"), "James", "Smith", $existingFamily);从测试方法进行调用。看起来您有时会使用不同的管理器,因此第二个管理器还不知道家庭,当您亲自cascade={persist}为家庭设置时,它会自动保留它并尝试重新插入。
| 归档时间: |
|
| 查看次数: |
331 次 |
| 最近记录: |