Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP]Testy w Laravel / php
Forum PHP.pl > Forum > Przedszkole
trifek
Witam serdecznie,
Uczę się pisania testów w Laravelu. Jako że nie wiem jak powinny wyglądać "profesjonalnie" - to chciałem dopytać czy to, co robię ma sens smile.gif

1. Test 1 - test sprawdzający czy strona działa poprawnie
  1. class HomeTest extends TestCase
  2. {
  3. public function testHomePageWorkCorrectly()
  4. {
  5. //$this->withoutExceptionHandling();
  6. $response = $this->get('/');
  7.  
  8. $response->assertStatus(200);
  9. }
  10. }



2. Test 2 - test logowania z dobrym i błędnym hasłem
  1. class LoginTest extends TestCase
  2. {
  3. public function testUserCanLoginWithCorrectPassword()
  4. {
  5. //$this->withoutExceptionHandling();
  6. $response = $this->post(route('cms.check_admin_login'), [
  7. 'email' => 'lukpeta@icloud.com',
  8. 'password' => 'passw'
  9. ]);
  10. $response->assertRedirect('/psCMS/');
  11. $this->assertTrue(Auth::check());
  12. }
  13.  
  14. public function testUserCannotLoginWithInCorrectPassword()
  15. {
  16. //$this->withoutExceptionHandling();
  17. $response = $this->post(route('cms.check_admin_login'), [
  18. 'email' => 'lukpeta@icloud.com',
  19. 'password' => 'xxxxx'
  20. ]);
  21. $response->assertRedirect('psCMS/login?error=1');
  22. $this->assertFalse(Auth::check());
  23. }
  24. }



3. Test 3 - test modułu reklam. Dodawanie nowej reklamy, sprawdzanie czy istnieje, pobieranie listy reklam, sprawdzanie czy walidacja pól działa, kasowanie
  1. class AdTest extends TestCase
  2. {
  3. use \Illuminate\Foundation\Testing\WithFaker;
  4.  
  5. public function testCreateAd()
  6. {
  7. $user = User::find(1);
  8. $this->be($user);
  9.  
  10. $ad = factory(Ad::class)->create();
  11. $this->post(route('ads.edit') . '/' . $ad->id, $ad->toArray());
  12. $this->assertDatabaseHas('ads', ['id' => $ad->id]);
  13. }
  14.  
  15. public function testAdExist()
  16. {
  17. $user = User::find(1);
  18. $this->be($user);
  19.  
  20. $ad = factory(Ad::class)->create();
  21. $this->post(route('ads.edit') . '/' . $ad->id, $ad->toArray());
  22. $this->assertDatabaseHas('ads', ['id' => $ad->id]);
  23. }
  24.  
  25.  
  26. public function testGetAdsList()
  27. {
  28. $user = User::find(1);
  29. $this->be($user);
  30.  
  31. $ads = factory(Ad::class)->create();
  32. $response = $this->get(route('ads.index'));
  33. $response->assertSee($ads->title);
  34. }
  35.  
  36. public function testCreateAdMustBeCompleted()
  37. {
  38. $user = User::find(1);
  39. $this->be($user);
  40.  
  41. $response = $this->actingAs($user)->post(route('ads.store'), [
  42. 'title' => null,
  43. 'provincial_id' => null,
  44. 'content' => null,
  45. ]);
  46.  
  47. $response->assertSessionHasErrors(['title', 'provincial_id', 'content']);
  48. }
  49.  
  50. public function testCreateAdFromForm()
  51. {
  52. $user = User::find(1);
  53. $this->be($user);
  54. $faker = Faker\Factory::create('pl_PL');
  55. $title = $faker->sentence($nbWords = 6, $variableNbWords = true);
  56. $response = $this->actingAs($user)->post(route('ads.store'), [
  57. 'title' => $title,
  58. 'content' => $faker->text($maxNbChars = 200),
  59. 'provincial_id' => $faker->numberBetween(1, 8),
  60. ]);
  61.  
  62. $this->assertDatabaseHas('ads', ['title' => $title]);
  63. }
  64.  
  65.  
  66. public function testEditAdFromForm()
  67. {
  68. $this->withExceptionHandling();
  69. $user = User::find(1);
  70. $this->be($user);
  71. $faker = Faker\Factory::create('pl_PL');
  72.  
  73. $ad = factory(Ad::class)->create();
  74. $id = $ad->id;
  75.  
  76. $title = $faker->sentence($nbWords = 6, $variableNbWords = true);
  77. $response = $this->actingAs($user)->post(route('ads.update') . '/' . $id, [
  78. 'title' => $title,
  79. 'content' => $faker->text($maxNbChars = 200),
  80. 'provincial_id' => $faker->numberBetween(1, 8),
  81. 'id' => $id
  82. ]);
  83.  
  84. //dd($response->getContent());
  85.  
  86. $this->assertDatabaseHas('ads', ['title' => $title, 'id' => $id]);
  87. }
  88.  
  89.  
  90. public function testDeleteAd()
  91. {
  92. //$this->withExceptionHandling();
  93. //$this->withoutExceptionHandling();
  94. $user = User::find(1);
  95. $this->be($user);
  96.  
  97. $ad = factory(Ad::class)->create();
  98. $id = $ad->id;
  99.  
  100. $faker = Faker\Factory::create('pl_PL');
  101. $title = $faker->sentence($nbWords = 6, $variableNbWords = true);
  102. $response = $this->actingAs($user)->post(route('ads.destroy'), [
  103. 'title' => $title,
  104. 'content' => $faker->text($maxNbChars = 200),
  105. 'provincial_id' => $faker->numberBetween(1, 8),
  106. //'id'=> $id,
  107. 'id' => [$id]
  108. ]);
  109.  
  110. $this->assertDatabaseMissing('ads', ['id' => $ad->id]);
  111. }
  112.  
  113.  
  114. }



Czy takie testy są poprawne? Czy raczej powinny wyglądać inaczej?smile.gif
Pyton_000
Takie testy są ok. Nazywają się testami Integracyjnymi lub akceptacyjnymi ( w zależności kto patrzy smile.gif )
Generalnie tutaj testujesz czy funkcjonalność zadana w ogóle działa czyli zapisujesz i sprawdzasz czy się zrobił.

Do tego możesz dołożyć testy Unit które będą testować wybraną klasę z logiką czyli np. masz klasę która generuje Ci coś a twoim zadaniem jest sprawdzić czy klasa będzie się zachowywała w najróżniejszych przypadkach czyli jak dasz dobre wartości to dobry wynik, jak dasz złe wartości to błąd, jak nie dasz wartości to błąd itd.
Rysh
Tak jak napisał kolega powyżej, testy są OK - ale można je napisać o wiele lepiej.
  1. $this->assertAuthenticated()

zamiast:
  1. $this->assertTrue(Auth::check());


Tutaj widać, że budowanie URL leży:
  1. $this->post(route('ads.edit') . '/' . $ad->id, $ad->toArray());


Fakera nie musisz tworzyć za każdym razem, masz przecież trait'a którego możeszużyć:
  1. <?php
  2.  
  3. namespace Tests\Feature;
  4.  
  5. use Illuminate\Foundation\Testing\WithFaker;
  6. use Tests\TestCase;
  7.  
  8. /**
  9.  * Class WebControllerTest
  10.  */
  11. class WebControllerTest extends TestCase
  12. {
  13. use WithFaker;
  14.  
  15. public function testFaker(): void
  16. {
  17. $this->faker->email;
  18. }
  19. }
trifek
Cytat(Rysh @ 5.04.2020, 21:04:57 ) *
Tak jak napisał kolega powyżej, testy są OK - ale można je napisać o wiele lepiej.
  1. $this->assertAuthenticated()

zamiast:
  1. $this->assertTrue(Auth::check());


Tutaj widać, że budowanie URL leży:
  1. $this->post(route('ads.edit') . '/' . $ad->id, $ad->toArray());


Fakera nie musisz tworzyć za każdym razem, masz przecież trait'a którego możeszużyć:
  1. <?php
  2.  
  3. namespace Tests\Feature;
  4.  
  5. use Illuminate\Foundation\Testing\WithFaker;
  6. use Tests\TestCase;
  7.  
  8. /**
  9.  * Class WebControllerTest
  10.  */
  11. class WebControllerTest extends TestCase
  12. {
  13. use WithFaker;
  14.  
  15. public function testFaker(): void
  16. {
  17. $this->faker->email;
  18. }
  19. }


Dziękuję bardzo za uwagi / opinie smile.gif


  1. $this->assertFalse(Auth::check());

A jak taki test zapisać? smile.gif

Cytat(Pyton_000 @ 5.04.2020, 19:44:31 ) *
Takie testy są ok. Nazywają się testami Integracyjnymi lub akceptacyjnymi ( w zależności kto patrzy smile.gif )
Generalnie tutaj testujesz czy funkcjonalność zadana w ogóle działa czyli zapisujesz i sprawdzasz czy się zrobił.

Do tego możesz dołożyć testy Unit które będą testować wybraną klasę z logiką czyli np. masz klasę która generuje Ci coś a twoim zadaniem jest sprawdzić czy klasa będzie się zachowywała w najróżniejszych przypadkach czyli jak dasz dobre wartości to dobry wynik, jak dasz złe wartości to błąd, jak nie dasz wartości to błąd itd.


W firmach, które testy najczęściej się pisze? Pewnie te integracyjne? Unit testy idą do klas "nie laravelowych?" smile.gif
Rysh
Z tego co zaobserwowałem, to w firmach rzadko kiedy piszę się testy wink.gif

Unit: testy które testują daną klasę - tylko klasę, dajesz input do metody i sprawdzasz czy wynik jest spełnia Twoje oczekiwania, nie interesuje Cie za bardzo reszta.
Feature: to testy które testują żądania do strony i oczekiwany response, czyli wysyłasz formularz, odbierasz błąd walidacji lub zwrócone dane. Następnie sprawdzasz czy zwrócone dane są zgodne z oczekiwaniami, sprawdzasz również status HTTP (w przypadku REST API - bardzo ważne) i inne rzeczy (z czasem sam ogarniesz, co testować a czego nie).

Co jest bardzo przydatne moim zdaniem, to code coverage - puszczasz testy na całość, i pokazuje Ci miejsca których nie masz przetestowanych. Czyli żaden kod podczas wykonywania testów nie wszedł do danej instrukcji - czyli przewidziałeś pewną sytuację, ale nie przetestowałeś jej. Tak to wygląda w IDE:


Linijka 45 nie została przetestowana, 44 i 48 już tak.

Warto sobie po testować całe klasy (unit) wraz z kontrolerami (feature). Niektórych czasem nie ma sensu, ale to od programisty zależy. I oczywiście nie testujesz obcych paczek, tylko swój kod który napisałeś.

Pisanie testów, owocuje w przyszłości, gdy zmienisz drobną rzecz, może się okazać że resposne się zmienił - testami to wychwycisz. Bez wychwycenia zmiany response, front może się posypać - czego nikt by nie chciał.
No i zmiany commitujesz dopiero jak wszystkie testy są wykonane z sukcesem. Tutaj warto sobie dodać również automatyczne wykonywanie testów po przesłaniu zmian na GIT, więc nawet jak zapomnisz puścić testów lokalnie, to gitlab czy inny, poinformuje Cię o tym.

Cytat
A jak taki test zapisać? smile.gif


Ja bym zapisał to tak, ale co programista to inny sposób - ja staram się używać, tego co mi podpowiada IDE.
  1. $this->assertFalse($this->isAuthenticated());
markonix
Tutaj parę ściąg, aby rozróżnić integracyjne od jednostkowych:
https://www.google.com/search?q=2+unit+test...off&tbm=vid
smile.gif

Osobiście, ze względu na braki kadrowo/czasowe pisze głównie integracyjne, są one bardziej praktyczne, weryfikują działanie podobnie do testera manualnego, więc po prostu robią to co początkujący programista robi sam po jakimś update - przeklikuje system.
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-2025 Invision Power Services, Inc.