Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [SF][Symfony2][Symfony]problem z testowaniem usług / service
Forum PHP.pl > Forum > PHP > Frameworki
szypi1989
Mam nadzieje, że mnie zrozumiecie. Otóż mam problem z testowaniem service . Te service ma parametry , które są wstrzykiwane . Znów te service składa się 2 następnych service (bo one tam do tego głównego service są wstrzykiwane).
Te 2 następne service znów mają jakieś elementy wstrzykiwane . I irytuje mnie to , że muszę jawnie tak wrzucać paramtery / obiekty, bo w testach nie można wstrzykiwać. Czy istnieje jakiś sposób aby to obejść?
rad11
Nie do końca rozumiem Twój problem ale wydaje mi się, że wystarczy jak użyjesz np:

  1.  
  2. $argument1 = $this->getMockBuilder(Service1::class)
  3. ->disableOriginalConstructor()
  4. ->getMock();
  5.  
  6. $argument2 = $this->getMockBuilder(Service2::class)
  7. ->disableOriginalConstructor()
  8. ->getMock();
  9.  
  10. $argument3 = $this->getMockBuilder(Service3::class)
  11. ->disableOriginalConstructor()
  12. ->getMock();
  13.  
  14. $service = new Service(
  15. $argument1,
  16. $argument2,
  17. $argument3
  18. );
  19.  


chodzi o to abyś po mock`ował sobie wszystkie potrzebne rzeczy
szypi1989
No super . Dzięki. A wiesz może jak przetestować funkcję , która używa danych z requesta? Bo wtedy test mi wrzuca błąd ponieważ żadne dane nie są wysyłane?
Nie wiem , czy wiesz o co chodzi .
Mam tutaj dla przykładu kawałek klasy service:
  1. class FetchMsgSql {
  2.  
  3. protected $entityManager;
  4. protected $qb;
  5. protected $parameters;
  6. protected $array_par;
  7. private $requestStack;
  8.  
  9. public function __construct(EntityManager $em, RequestStack $requestStack) {
  10. $this->requestStack = $requestStack;
  11. $this->entityManager = $em;
  12. $this->qb = $em->createQueryBuilder();
  13. }
  14.  
  15. // create results based on data from the request table
  16. // converting data to the form of query
  17. public function getSql() {
  18. $this->qb->select('a')->from('AppBundle:Cars', 'a');
  19. // starts the build_condition function if a query was sent from the searchAction action and the knppaginaton object uses the search function
  20. // search == 1 -> when you view the pages by knppagination and knppagination is supposed to store the results
  21. // search == 1 -> results from the request table are attached during the search engine save in the sortable_link.html view
  22. (($this->requestStack->getCurrentRequest()->query->get('search') == "1") || ($this->requestStack->getCurrentRequest()->getRealMethod() == 'POST') ) ? $this->qb->setParameters($this->build_condition()) : NULL;
  23. $dql = $this->qb->getQuery()->getDQL();
  24. //< convert the dql object to the query form
  25. foreach ($this->qb->getParameters() as $index => $param) {
  26. $dql = str_replace(":" . $param->getName(), $param->getValue(), $dql);
  27. $dql = str_replace("LIKE " . $param->getValue(), "LIKE '" . $param->getValue() . "'", $dql);
  28. }
  29. // >
  30. $query = $this->entityManager->createQuery($dql);
  31. return $query;
  32. }


I Chciałbym przetestować funkcję getSql() . No ale w testach nie mogę, bo nie mam tych danych z requestu. Musiałbym jakoś je ustawić wcześniej .
rad11
Według mnie powinieneś zamockować po prostu tą metodę z odpowiednio zwracanymi danymi które są używane gdzieś dalej, nie widzę sensu abyś miał łączyć się do bazy danych itd aby przetestować Ją samą.

Ale jak chciałbyś utworzyć te parametry to musiałbyś zrobić mniej więcej tak:

  1. $requestStack = new RequestStack();
  2. $requestStack->getCurrentRequest()->setMethod('POST')
  3. $requestStack->getCurrentRequest()->query->set('search', 1);


I to przekazać jako parametr ale tak jak mówię dla mnie testowanie tej metody nie ma sensu
nospor
1) Robiac Mock klasy request mozesz okreslic co maja zwracac dane metody a tym samym mozesz zasymulowac przeslanie parametrow z POST
2) Moim zdaniem masz zle zaprojektowana te klase bo jest ona zalezna od request. Bardziej metoda getSql powinna jako parametr przyjmowac tablice i tam powinienies ustawiac dane z request. Wowczas uniezalezniasz sie od klasu request, twoj service moze dzialac na danych z dowolnego zrodla i bez problemu jestes to w stanie przetestowac

ps:
$dql = str_replace(":" . $param->getName(), $param->getValue(), $dql);
$dql = str_replace("LIKE " . $param->getValue(), "LIKE '" . $param->getValue() . "'", $dql);
Ouch, czemu tak?
szypi1989
Używam knpbundle a on niestety potrzebuje dane trochę bardziej przekowertowane. Zresztą dużo by mówić, po prostu jest coś gdzieś w kodzie kombinowane.
Jeśli tak uważasz, to postaram się to pozmieniać w przyszłości i zmienić trochę model tego. Uważam, że masz racje i źle to zrealizowałem .
Chyba masz racje, nie będę tego testował. Mam słabe doświadczenie w programowaniu i po prostu się uczę testować moją aplikację ;-)

Call to a member function setMethod . Funkcja ta niestety nie działa :-(
rad11
A jaka dokładnie wersja Symfony ?
szypi1989
symfony 3.4 . Jakoś nie łapie tego request.Ponieważ jak sprawdzam var_dump($requestStack->getCurrentRequest()) to zwraca mi NULL ale tylko w kodzie testach. A normalnie w kodzie aplikacje zwraca obiekt request. Może coś w kernellu trzeba ustawić?
rad11
To spróbuj zrobić coś takiego:

  1. $request = new Request(['search' => 1],null,null,null,null,['REQUEST_METHOD' => 'POST']);
  2.  
  3. $requestStackMock = $this->getMockBuilder(RequestStack::class)
  4. ->disableOriginalConstructor()
  5. ->getMock();
  6.  
  7. $requestStackMock->expects($this->any())->method('getCurrentRequest')->willReturn($request);
  8.  


i to przekazać jako parametr
Crozin
@rad11 Ale po co tak kombinować?
  1. $request = new Request(...);
  2. $requestStack = new RequestStack();
  3. $requestStack->push($request);
Tak długo jak długo nie trzeba używać żadnych mocków powinno się od nich uciekać. Request / RequestStack to bardzo proste obiekty, które spokojnie można tworzyć sobie na potęgę.

@szypi1989 Tutaj testy dają Ci nawet coś więcej niż pewność, że kod działa. Fakt, że jest Ci je tak trudno napisać ładnie obrazuje to, że kod jest - wybacz za bezpośredniość - słabo napisany. Zadaniem Twojej klasy jest zbudowanie odpowiedniego zapytania SQL/DQL na podstawie jakichś kryteriów. No to w takim razie skup się na tym by robił to i tylko to, korzystając z prostych i specyficznych dla tego problemu (zadania) typów danych.
  1. class AbcSearchCriteria {
  2. private string $name;
  3. private string $value;
  4.  
  5. public __construct(string $name, string $value) { ... }
  6.  
  7. public getName() { return $this->name; }
  8. public getValue() { return $this->value; }
  9. }
  10.  
  11. class AbcSearchQueryBuilder {
  12. public __construct($em, ...)
  13.  
  14. public buildQuery(AbcSearchCriteria $criter) { ... }
  15. }
To że w produkcyjnym kodzie te kryteria będą gdzieś, jakoś budowane na podstawie danych z żądania HTTP nie ma znaczenia dla tej klasy - ona ma się skupić na swoim zadaniu czyli budowaniu odpowiedniego zapytania na podstawie odpowiednich kryteriów. Ten kod strasznie łatwo przetestujesz i co więcej może to być wartościowy test! A co z tą kwestią tworzenia obiektu reprezentującego kryteria? To jest inna część kodu - może być inicjowana gdzieś pewnie na poziomie jakiegoś kontrolera, która powinna być poddana osobnemu testowi - który weryfikuje jedynie czy obiekt kryteriów jest prawidłowo tworzony na podstawie obiektu żądania HTTP.
szypi1989
rozumiem o co Tobie chodzi . No ale mam słabe doświadczenie w programowaniu a już sam fakt , że źle zacząłem, że dopiero podchodzę teraz do testów to już z tego wynikają błędy. Teraz sam to widzę
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.