2015-01-12 6 views
5

Ich stehe derzeit vor einem großen Problem. Während eines Komponententests verwende ich Fixtures zu Beginn des Tests, um Daten in eine Datenbank hinzuzufügen (alles funktioniert einwandfrei), und ich möchte den gleichen Code wie das Fixture verwenden, um Daten während des Tests hinzuzufügen. Exemple:Symfony2, Fixtures und Unit Test. Hinzufügen von Testdaten während des Tests in einer ManyToMany-Beziehung

Entity Person:

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; 
    } 
  
    [...] 
  
} 

Entity Familie:

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; 
  
    [...] 
  
} 

Leuchten:

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

Einheit Test:

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'])); 
     [...] 
    } 
  
} 

Wenn ich den Test auszuführen, so erhält i diesen Fehler:

Lehre \ DBAL \ Exception \ UniqueConstraintViolationException: Eine Ausnahme beim ‚INSERT INTO-Familie (slug, name, erstellt, last_updated_date, deleteddate) VALUES Ausführung (?,?,?,?,?) mit params ["Smith", "Smith", "2015-01-10 13:59:28", "2015-01-10 13:59:28" , null]: SQLSTATE [23000]: Integrität Einschränkungsverletzung: 1062 DUPLICATA du champ 'Smith' pour la clef 'UNIQ_A5E6215B989D9B62'

Dieser Fehler besagt, dass das Feld "slug" eindeutig sein muss. Es wird versucht, eine neue Familie hinzuzufügen, obwohl die Familie bereits existiert. Es sollte nur eine Beziehung zwischen der neuen Person (James Smith) und der Familie hinzufügen. Ich verstehe nicht warum! Irgendeine Idee?

Antwort

0

Lassen Sie mich es Schritt für Schritt nehmen, folgt hier ein wenig mehr, als Sie gefragt, aber ich hoffe, es werden Ihnen helfen, sauberere Tests zu schreiben, wie ich genug schlechte an meinem Arbeitsort gesehen habe:

Erstens ist dies kein Unit-Test, nur damit Sie es wissen. Es heißt Funktionstest, weil Sie die gesamte Systemfunktionalität testen und nicht eine kleine Einheit, die eine Klasse wäre.

Zweitens, erstellen Sie keinen neuen Client (createClient-Aufruf), nur um einen Container zu erhalten. Darüber hinaus benötigen Sie normalerweise nicht mehr als einen Client pro Testfall. Wenn Sie es wirklich in der setUp-Methode erstellen wollen, dann speichern Sie es im Feld TestCase (erstellen Sie ein geschütztes Feld in Ihrer TestCase-Klasse, das Sie dafür verwenden) und speichern Sie es in der Methode setUp, damit Sie den Testfall verwenden können. Das ist notwendig, weil Sie irgendwann Fehler in Ihrer Testsuite einführen, indem Sie mehr als einen Container instanziieren (createClient instanziiert bei jedem Aufruf einen neuen Container) und mehr als einen EntiitManager zum Beispiel besitzt. Dann werden Sie irgendwann einige Entitäten haben, die zu einem EntityManager gehören, und andere zu einem anderen (was möglich ist zu verwalten, aber wenn Sie nicht wissen, wie Doctrine funktioniert, ist es besser, es nicht für Sie zu konfrontieren).

Drittens brauchen Sie in Ihrem Beispiel gar nicht Fixture Loader. Sie müssen auch nicht purge hier anrufen, weil es automatisch vom Executor aufgerufen wird. So könnte Ihr Code in setUp aussehen:

Ich denke, es ist viel einfacher so.

Auch ich bin mir nicht sicher, warum Sie Ihr Gerät ContainerAware, wie Sie Container nicht verwenden müssen, soweit ich sehen kann und in den meisten Fällen müssen Sie eigentlich nicht.

Viertens: Speichern Sie EntityManager niemals in der Fixture-Instanz. Es soll durch load Methode gehen, es verwenden, um alles zu laden und es vollständig zu vergessen.

Versuchen Sie, Ihre Fixture-Methoden so zu ändern, dass Manager als Argument erwartet wird, anstatt sie als Feld zu verwenden, und rufen Sie (new Fixture1)->addPerson($this->client->getKernel()->getContainer()->get("doctrine.orm.default_entity_manager"), "James", "Smith", $existingFamily); von Ihrer Testmethode auf. Es sieht so aus, als ob Sie irgendwann verschiedene Manager benutzen, so dass der zweite noch nichts über Familie weiß und wenn Sie cascade={persist} für Familien in Person einrichten, bleibt es automatisch bestehen und versucht erneut einzufügen.

Verwandte Themen