2015-01-12 15 views
5

Mam obecnie duży problem. Podczas testu jednostkowego używam urządzeń na początku testu, aby dodać dane do bazy danych (wszystko działa poprawnie) i chciałbym użyć tego samego kodu co urządzenie, aby dodać dane podczas testu. Exemple:Symfony2, urządzenia i test jednostki. Dodaj dane testowe podczas testu w ManyToMany Relation

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

Podmiot Rodzina:

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

Oprawy: test

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

Jednostka:

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

Kiedy wykonać test, i uzyskać ten błąd:

Doctrine \ DBAL \ Wyjątek \ UniqueConstraintViolationException: AN wyjątek podczas wykonywania „INSERT INTO rodziny (ślimak, nazwy tworzone, last_updated_date, deleteddate) Wartości (?,?,?,?,?) "z parami [" kowal "," Smith "," 2015-01-10 13:59:28 "," 2015-01-10 13:59:28 " , zero] SQLSTATE [23000] naruszenie integralności ograniczenia: 1062 Duplicata du Champ "Smitha wlać Klucz 'UNIQ_A5E6215B989D9B62'

Ten błąd oznacza, że ​​pole "slug" musi być unikalne. Próbuje dodać nową rodzinę, mimo że rodzina już istnieje. Powinno to po prostu dodać relację między nową Osobą (James Smith) a Rodziną. Nie rozumiem dlaczego! Każdy pomysł?

Odpowiedz

0

Pozwól mi wziąć to krok po kroku, tutaj następuje trochę więcej niż prosiłaś, ale mam nadzieję, że to pomoże Ci napisać czystszych testy jak widziałem wystarczająco dużo złych miejscach na moich prac:

Po pierwsze, nie jest to test jednostkowy, tylko dlatego, że wiesz. Nazywa się to testem funkcjonalnym, ponieważ testujesz całą funkcjonalność systemu, a nie małą jednostkę, która byłaby klasą.

Po drugie, nie twórz nowego klienta (wywołanie createClient) tylko w celu uzyskania kontenera. Zwykle nie potrzebujesz więcej niż jednego klienta na test. Jeśli naprawdę chcesz go utworzyć w metodzie setUp, zapisz go w polu TestCase (utwórz pole chronione w klasie TestCase, którego będziesz używał) i zapisz je w metodzie setUp, aby można było użyć tego przypadku testowego. Jest to potrzebne, ponieważ w pewnym momencie wprowadzisz błędy do swojego zestawu testów, tworząc instancje więcej niż jednego kontenera (createClient tworzy nowy kontener za każdym razem, gdy go wywołujesz) i mając na przykład więcej niż jednego EntitiManagers. W pewnym momencie będziesz miał kilka podmiotów, które należą do jednego EntityManager, a inne do innego (które można zarządzać, ale jeśli nie wiesz, jak działa Doctrine, lepiej nie stawić mu czoła).

Po trzecie, w ogóle nie potrzebujesz programu ładującego urządzenia. Również nie musisz dzwonić pod numer purge, ponieważ jest on automatycznie wywoływany z executora. Tak może wyglądać Twój kod w 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(); 

Myślę, że w ten sposób jest o wiele prostszy.

Nie jestem również pewien, dlaczego chcesz, aby urządzenie było kontenerem, ponieważ nie korzystasz z kontenera, o ile widzę, aw większości przypadków nie musisz tego robić.

Po czwarte, nigdy nie przechowuj EntityManager w instancji Urządzenia. Powinien dostać się za pomocą metody load, użyć jej do załadowania wszystkiego i całkowitego zapomnienia.

Spróbuj zmienić metody urządzeń, aby oczekiwać menedżera jako argumentu zamiast używać go jako pola i zadzwoń pod numer (new Fixture1)->addPerson($this->client->getKernel()->getContainer()->get("doctrine.orm.default_entity_manager"), "James", "Smith", $existingFamily); ze swojej metody testowej. Wygląda na to, że w pewnym momencie możesz użyć różnych menedżerów, więc drugi nie wie jeszcze o rodzinie, a po skonfigurowaniu cascade={persist} dla rodzin w kategorii Person automatycznie go utrzymuje i próbuje ponownie wstawić.

Powiązane problemy