Cum fac Testing în Laravel?

Buna ziua,
Invat Laravel de o vreme. Acum sunt la partea de testare, mai exact HTTP request testing. Pentru ca ceea ce este pe net are un nivel mult prea mare de dificultate pentru neinitiati, am incercat sa gasesc singura o solutie. Poate vreti sa imi spuneti care sunt neajunsurile in codul de mai jos: (mentionez ca el e ok, ruleaza si nu returneaza erori, dar eu vreau sa stiu daca este ok acest mod de abordare)

public function testUserTest()
    {
            $user=\App\User::first();
            $employee=\App\Employee::first();
            $report=\App\Report::first();

            if(isset($user))
            {
                     $response=$this->actingAs($user)
                                               ->post('report');

                     if(isset($report))
                     {
                                  $response=$this->actingAs($user)
                                                            ->get('report/{report}/addind');

                                  if(isset($employee))
                                  {
                                   $response=$this->actingAs($user)
                                                             ->get('calculate/{employee}/{report}');
                                   }
                      }
            }
            
          $this->assertTrue(true);
    }

Regula nr. 1 a testelor este că nu trebuie să ai logică în teste. Adică acele if(isset()) nu își au locul aici.

Cum aș face eu:

  1. În ce situație NU ar fi setat $user? Ai test pentru acel caz?
  2. în ce situație NU ar fi setat $report? Ai test și pentru acel caz?
  3. etc.

Tu aici testezi doar happy path, dar nu ai teste pentru altceva.

Regula nr. 2 este că unit testing-ul se aplică la nivel de… unitate. Fără să-ți știu restul codului, tu testezi acolo trei unități: User, Employee și Report. Ce ai tu aici seamănă mai mult a test de… integrare (cred).


PS: nu sunt foarte sigur, dar cred că în momentul în care ai $user = ceva, isset($user) va întoarce întotdeauna true.

2 Likes

Merci! Am inteles!
HTTP request trebuie sa fie in ordine si daca nu sunt setate $user si $report (practic cererile conduc spre pagini de eroare)
Se recomanda sa scriu cate o functie pentru fiecare unitate, separat.

Asta dacă faci unit testing. Dacă îmi aduc aminte bine, Laravel vine cu Behat și se testează ceva mai mult decât unitățile, dar… nu sunt sigur :slight_smile:

Îți recomand să îți documentezi procesul de învățare și să lași notițe ori aici or pe blogul DevForum :wink:

1 Like

Sunt obisnuita sa imi iau notite pe caiet dar o sa fac o sinteza si o sa postez in continuare aici, in speranta ca mai gasesc pe cineva din aceassta comunitate care in acest moment este interesat de acest subiect.

2 Likes

Prima data scrii un test in care request e gol, si te asiguri ca in cod acest caz e tratat.
Dedesubt scrii un test in care introduci ceva incorect la user si pt ca nu ti-l poate scoate din db tratezi cazul acela.
Apoi pui cazul in care pui ceva valid in request pt user…
… si tot asa pana ai acoperit toate situatiile.
Ultimul e testul cu date corecte in request.

3 Likes

In cazul tau (testing HTTP requests) trebuie sa testezi cate un request separat (tu ai 3). Anyway, some tips:

  • Majoritatea testelor urmeaza formatul asta:
  1. Ma asigur ca am cu ce lucra (creez user, employee, raport, sau ce mai am nevoie pentru test).
  2. Fac ceva (in cazul tau, faci un request: get, post, put/patch, delete)
  3. Verific ca sistemul reactioneaza corect/cum ar trebui la ce am facut (punctul 2)
  • Asigura-te ca atunci cand lucrezi cu baza de date cureti dupa tine. Vezi trait-ul DatabaseMigrations.
  • Assertul tau e ca true e… true. Cand testezi request-uri ar trebui sa verifici raspunsul acestora (status code + text (vezi assertSee)) si daca se salveaza/sterg chestii din bd.
2 Likes

Am mai avansat de atunci :), multumita in mare parte voua.

Cazul meu concret, care este altul decat cel din primul mesaj, raspunde celor trei cerinte precizate de tine, astfel:

  1. nu am nevoie de nici o entitate
  2. fac un POST request
    3, in ceea ce priveste HTTP testing, mai am de acoperit cateva posibilitati privind datele de intrare.
    In prezent, sunt pe aici
<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Http\Controllers\IndicatorController;
use Illuminate\Http\Request;
class CalculateTest extends TestCase
{ use DatabaseTransactions;
	use WithoutMiddleware;
    /**
     * A basic test example.
     *
     * @return void
     */
    


 

   
     public function test_new_indicator_created_and_listed_and_success_message_send()
    { 
  
       $max_target=array(90,100,150);
       $value=array(0.9,1,1.5);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>$max_target,'value'=>$value);
	   $response = $this->call('POST', '/newindicator', $req);
	   $sessionData = session()->all();
	   $response->assertStatus(200)
       			 ->assertSee('precision');
       return $this->assertTrue($sessionData['status']=='Indicator adaugat!');
       			
    }
    public function test_fara_prag_de_stare_1_message_send()
    { 
       $max_target=array(90,100,150);
       $value=array(1);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>$max_target,'value'=>null);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertStatus(200)
       			 ->assertDontSee('precision');
	   $sessionData = session()->all();
       return $this->assertTrue($sessionData['status']=='Fara prag de stare');
       			
    } 
    public function test_fara_prag_de_stare_2_message_send()
    { 
      
       $value=array(1);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>null,'value'=>null);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertStatus(200)
       			 ->assertDontSee('precision');
	   $sessionData = session()->all();
       return $this->assertTrue($sessionData['status']=='Fara prag de stare');
       			
    }
     public function test_fara_prag_de_stare_3_message_send()
    { 
       $value=array(0.9,1,1.5);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>null,'value'=>$value);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertStatus(200)
       			 ->assertDontSee('precision');
	   $sessionData = session()->all();
       return $this->assertTrue($sessionData['status']=='Fara prag de stare');
       			
    }
     
    public function test_prag_de_stare_incorect_1_message_send()
    { 
       $max_target=array(90,100,150);
       $value=array('a',1,1.5);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>$max_target,'value'=>$value);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertStatus(200)
       			 ->assertDontSee('precision');
	   $sessionData = session()->all();
       return $this->assertTrue($sessionData['status']=='Prag de stare incorect');
       			
    }
     public function test_prag_de_stare_incorect_2_message_send()
    { 
       $max_target=array(90,'a',150);
       $value=array(0.9,1,1.5);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>$max_target,'value'=>$value);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertStatus(200)
       			 ->assertDontSee('precision');
	   $sessionData = session()->all();
       return $this->assertTrue($sessionData['status']=='Prag de stare incorect');
       			
    }
    public function test_prag_de_stare_incorect_3_message_send()
    { 
       $max_target=array(null,100,150);
       $value=array(0.9,1,1.5);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>$max_target,'value'=>$value);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertStatus(200)
       			 ->assertDontSee('precision');
	   $sessionData = session()->all();
       return $this->assertTrue($sessionData['status']=='Prag de stare incorect');
       			
    }
    public function test_prag_de_stare_incorect_4_message_send()
    { 
       $max_target=array(90,100,150);
       $value=array(null,1,1.5);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>$max_target,'value'=>$value);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertStatus(200)
       			 ->assertDontSee('precision');
	   $sessionData = session()->all();
       return $this->assertTrue($sessionData['status']=='Prag de stare incorect');
       			
    }

    public function test_bonus_value_bad_type_validation_fail_Test()
    { 

       $max_target=array(90,100,150);
       $value=array(0.9,1,1.5);
       $req= array('name'=>'precision','bonus_value'=>'alpha','max_target'=>$max_target,'value'=>$value);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertRedirect('/')
       			 ->assertDontSee('precision');
	   $sessionErrors = session()->get('errors');
	   return $this->assertTrue($sessionErrors->has('bonus_value'));
       		
    }

    public function test_name_value_validation_fail_test()
    { 
    	
       $max_target=array(90,100,150);
       $value=array(0.9,1,1.5);
       $req= array('bonus_value'=>100,'max_target'=>$max_target,'value'=>$value);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertRedirect('/')
       			 ->assertDontSee('precision');
	   $sessionErrors = session()->get('errors');
	   return $this->assertTrue($sessionErrors->has('name'));
       		
    }
}

Am adaugat si un test pentru situatia in care request este gol:

public function test_empty_request_Test()
    { 

       
       $req= array('name'=>null,'bonus_value'=>null,'max_target'=>null,'value'=>null);
	   $response = $this->call('POST', '/newindicator', $req);
	   $response->assertRedirect('/')
       			 ->assertDontSee('precision');
	   $sessionErrors = session()->get('errors');
	   return $this->assertTrue($sessionErrors->has('bonus_value', 'name'));
       		
    }

Tema: Laravel testing; phpunit tool

Studiu de caz: tratarea unui formular

Formularea problemei: la o cerere HTTP pentru accesarea resursei ‘/indicator’ prin metoda ‘post’, în condițiile în care informațiile transmise de client sunt corecte și complete, rezultatele așteptate sunt: regăsirea în tabelul părinte ‘indicators’ si in tabelul copil ‘targets’ a înregistrărilor care conțin exact datele transmise de client, obținerea unui răspuns cu codul de stare 301, redirecționarea către ‘/indicator’ și popularea variabilei globale $_SESSION cu proprietatea ‘status’ având valoarea ‘Indicator adaugat!’

Soluția descoperită de mine:

public function test_new_indicator_created_and_stored_and_success_message_send(){
 	
 //Given :input data :
       $max_target=array(90,100,150);
       $value=array(0.9,1,1.5);
       $req= array('name'=>'precision','bonus_value'=>100,'max_target'=>$max_target,'value'=>$value)
//When:HTTP Post request
       $response=$this->call('POST', '/indicator', $req);
 //Then: new data exist in database
	 $this->assertDatabaseHas('indicators', [
        'name' => 'precision',
        'bonus_value' => '100'
    ]);
       	$this->assertDatabaseHas('targets', [
        'max_target' => '90',
        'value' => '0.9'
    ]);
       	 $this->assertDatabaseHas('targets', [
        'max_target' => '100',
        'value' => '1'
    ]);
       	$this->assertDatabaseHas('targets', [
        'max_target' => '150',
        'value' => '1.5'
    ]);
//and then also: the status code of request is 301 :
       	$response->assertStatus(301);
//and then also: the response is a redirect to http://localhost/indicator:
	$response->assertRedirect('/indicator');
//and then also:  a success message is send(during HTTP testing the session retain a parameter named 'status' with  value 'Indicator adaugat!') 
       $sessionData = session()->all();
       $this->assertTrue($sessionData['status']=='Indicator adaugat!'); 			
    }

Bibliografie :slight_smile:
https://laravel.com/docs/5.4/http-tests


https://phpunit.readthedocs.io/en/7.1/assertions.html

1 Like

Cred că treaba asta s-ar putea scrie ca:

$this->assertEquals($sessionData['status'], 'Indicator adaugat!');

https://phpunit.de/manual/6.5/en/appendixes.assertions.html#appendixes.assertions.assertEquals

2 Likes

Da.
Eu mi-as mai fi dorit si sa extrag in $sessionData o cantitate mult mai mica de informatie.
Oh, nu. E ok, ultima dată când am verificat conținutul ei cred că uitasem să golesc memoria cache.

Vezi și aici.

Via: Laravel Blog Contest articles

1 Like