Hvis du håper å lære hvorfor tester er fordelaktige, er dette ikke artikkelen for deg. I løpet av denne opplæringen vil jeg anta at du allerede forstår fordelene, og håper å lære hvordan du kan skrive og organisere tester i Laravel 4.
Versjon 4 av Laravel tilbyr alvorlige forbedringer i forhold til testing, sammenlignet med sin tidligere utgivelse. Dette er den første artikkelen i en serie som vil dekke hvordan man skriver tester for Laravel 4-applikasjoner. Vi starter serien ved å diskutere modelltesting.
Med mindre du kjører raske spørringer på databasen, lar Laravel din søknad forbli databasen agnostisk. Med en enkel driverendring kan applikasjonen din nå jobbe med andre DBMS (MySQL, PostgreSQL, SQLite, etc.). Blant standardalternativene tilbyr SQLite en merkelig, men svært nyttig funksjon: In-Memory databaser.
Med Sqlite kan vi sette databasetilkoblingen til :hukommelse:
, som vil øke hastigheten på våre tester på grunn av at databasen ikke finnes på harddisken. Videre vil produksjons- / utviklingsdatabasen aldri bli fylt med gjenværende testdata, fordi forbindelsen, :hukommelse:
, begynner alltid med en tom database.
Kort sagt: En in-memory database muliggjør rask og ren testing.
Innen app / konfig / testing
katalog, opprett en ny fil, navngitt database.php
, og fyll det med følgende innhold:
// app / config / testing / database.php 'sqlite', 'connections' => array ('sqlite' => array ('driver' => 'sqlite', 'database' => ': minne:', 'prefix' => ")
Det faktum at database.php
er plassert i konfigurasjonen testing
katalog betyr at disse innstillingene bare skal brukes når de er i et testmiljø (som Laravel automatisk angir). Som sådan, når søknaden din nås normalt, vil ikke i-minne databasen bli brukt.
Siden databasen i minnet alltid er tom når en tilkobling er laget, er det viktig å migrere databasen før hver test. For å gjøre dette, åpne app / tester / TestCase.php
og legg til følgende metode til slutten av klassen:
/ ** * Migrerer databasen og setter e-posten til "late". * Dette vil føre til at testene kjører raskt. * * / privat funksjon prepareForTests () Artisan :: call ('migrate'); Mail :: late (true);
MERK: Den
SETUP ()
Metoden utføres av PHPUnit før hver test.
Denne metoden vil forberede databasen, og endre statusen til Laravel Mailer
klasse til late som
. På den måten vil Mailer ikke sende noen ekte e-post når du kjører tester. I stedet vil det logge de "sendte" meldingene.
Å fullføre app / tester / TestCase.php
, anrop prepareForTests ()
innenfor PHPUnit SETUP ()
metode, som vil utføres før hver test.
Ikke glem det
ordnede :: SETUP ()
, som vi overskriver metoden til foreldreklassen.
/ ** * Standard forberedelse for hver test * * / offentlig funksjon setUp () foreldre :: setUp (); // Ikke glem dette! $ Dette-> prepareForTests ();
På dette punktet, app / tester / TestCase.php
bør se ut som følgende kode. Husk at createApplication
er opprettet automatisk av Laravel. Du trenger ikke å bekymre deg for det.
// app / tester / TestCase.php prepareForTests (); / ** * Oppretter applikasjonen. * * @return Symfony \ Component \ HttpKernel \ HttpKernelInterface * / offentlig funksjon createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; retur krever __DIR __. '/ ... / ... / start.php'; / ** * Migrerer databasen og setter e-posten til "late". * Dette vil føre til at testene kjører raskt. * / privat funksjon prepareForTests () Artisan :: call ('migrate'); Mail :: late (true);
Nå, for å skrive våre tester, bare utvide Testforsøk
, og databasen vil bli initialisert og migrert før hver test.
Det er riktig å si at i denne artikkelen vil vi ikke følge TDD prosess. Problemet her er didaktisk, med målet å demonstrere hvordan testene kan skrives. På grunn av dette valgte jeg å avsløre de aktuelle modellene først, og deretter deres relaterte tester. Jeg tror at dette er en bedre måte å illustrere denne opplæringen på.
Konteksten til denne demo-applikasjonen er en enkel blogg / CMS, som inneholder brukere (autentisering), innlegg og statiske sider (som vises i menyen).
Vær oppmerksom på at modellen utvider klassen, Ardent, i stedet for Eloquent. Ardent er en pakke som gjør det enkelt å validere ved lagring av modellen (se $ regler
eiendom).
Deretter har vi offentlig statisk $ fabrikk
array, som utnytter FactoryMuff-pakken, for å hjelpe til med opprettelse av objekt ved testing.
Både Ardentx og FactoryMuff er tilgjengelig gjennom Packagist og Composer.
I vår Post
modell, vi har et forhold til Bruker
modell, gjennom magien forfatter
metode.
Endelig har vi en enkel metode som returnerer datoen, formatert som "dag måned år".
// app / modeller / Post.php 'Required', // Post tittel 'slug' => 'Required | alpha_dash', // Post Url 'content' => 'Required', // Post innhold (Markdown) 'author_id' => 'required' numeric ' // Forfatter id); / ** * Array brukt av FactoryMuff for å lage Test objekter * / offentlig statisk $ factory = array ('title' => 'streng', 'slug' => 'streng', 'content' => 'tekst', 'author_id '=>' fabrikk | Bruker ', // Vil være ID for en eksisterende bruker.); / ** * Tilhører bruker * / offentlig funksjon forfatter () return $ this-> belongsTo ('User', 'author_id'); / ** * Få formatert innlegg dato * * @return streng * / offentlig funksjon postedAt () $ date_obj = $ this-> created_at; hvis (is_string ($ this-> created_at)) $ date_obj = DateTime :: createFromFormat ('Y-m-d H: i: s', $ date_obj); returnere $ date_obj-> format ('d / m / y');
For å holde orden på det, har jeg lagt klassen med Post
modell tester i app / prøver / modeller / PostTest.php
. Vi går gjennom alle tester, en seksjon av gangen.
// app / tester / modeller / PostTest.phpVi utvider
Testforsøk
klasse, som er et krav til PHPUnit testing i Laravel. Også, ikke glem vårtprepareTests
metode som vil løpe før hver test.offentlig funksjon test_relasjon_med_author () // Instantiate, fyll med verdier, lagre og returner $ post = FactoryMuff :: create ('Post'); // Takk til FactoryMuff, denne $ posten har en forfatter $ this-> assertEquals ($ post-> author_id, $ post-> author-> id);Denne testen er en "valgfri" en. Vi tester at forholdet "
Post
tilhørerBruker
". Formålet her er for det meste å demonstrere funksjonaliteten til FactoryMuff.Først når
Post
klassen har$ fabrikk
statisk matrise som inneholder'author_id' => 'fabrikk | bruker'
(Merk kildekoden til modellen, vist ovenfor) Installer FactoryMuff en nyBruker
fyller sine attributter, lagre i databasen og til slutt returnere sin id tilauthor_id
attributt iPost
.For at dette skal være mulig, skal
Bruker
Modellen må ha en$ fabrikk
array som også beskriver feltene sine.Legg merke til hvordan du får tilgang til
Bruker
forhold gjennom$ Post-> forfatter
. Som et eksempel kan vi få tilgang til$ Post-> autorise-> brukernavn
, eller andre eksisterende brukerattributter.FactoryMuff-pakken muliggjør rask etablering av konsistente objekter med det formål å teste, samtidig som man respekterer og instanser eventuelle nødvendige forhold. I dette tilfellet, når vi lager en
Post
medFactoryMuff :: lage ( 'Post')
deBruker
vil også bli forberedt og gjort tilgjengelig.offentlig funksjon test_posted_at () // Instantiate, fyll med verdier, lagre og returner $ post = FactoryMuff :: create ('Post'); // Regular uttrykk som representerer d / m / Y mønster $ expected = '/ \ d 2 \ / \ d 2 \ / \ d 4 /'; // True hvis preg_match finner mønsteret $ matches = (preg_match ($ expected, $ post-> postedAt ()))? sant: false; $ this-> assertTrue ($ matches);For å fullføre, avgjør vi om strengen returneres av
postedAt ()
Metoden følger "dag / måned / år" format. For slik verifisering brukes et regulært uttrykk for å teste om mønsteret\ D 2 \ / \ d 2 \ / \ d 4
("2 tall" + "bar" + "2 tall" + "bar" + "4 tall") er funnet.Alternativt kan vi bruke PHPUnits assertRegExp matcher.
På dette punktet, den
app / prøver / modeller / PostTest.php
filen er som følger:// app / tester / modeller / PostTest.php assertEquals ($ post-> author_id, $ post-> author-> id); offentlig funksjon test_posted_at () // Instantiate, fyll med verdier, lagre og returner $ post = FactoryMuff :: create ('Post'); // Regular uttrykk som representerer d / m / Y mønster $ expected = '/ \ d 2 \ / \ d 2 \ / \ d 4 /'; // True hvis preg_match finner mønsteret $ matches = (preg_match ($ expected, $ post-> postedAt ()))? sant: false; $ this-> assertTrue ($ matches);PS: Jeg valgte ikke å skrive navnet på testene i CamelCase for lesbarhetsformål. PSR-1 tilgi meg, men
testRelationWithAuthor
er ikke så lesbar som jeg personlig ville foretrekke. Du er fri til å bruke den stilen du mest foretrekker, selvfølgelig.Sidemodell
Vårt CMS trenger en modell som representerer statiske sider. Denne modellen er implementert som følger:
'Required', // Sidetittel 'slug' => 'Required | alpha_dash', // Slug (url) 'content' => 'nødvendig', // Innhold (markdown) 'author_id' => 'required' numeric ' , // Forfatter-ID); / ** * Array brukt av FactoryMuff * / offentlig statisk $ factory = array ('title' => 'streng', 'slug' => 'string', 'content' => 'text', 'author_id' => ' fabrikk | Bruker ', // Vil være ID for en eksisterende bruker.); / ** * Tilhører bruker * / offentlig funksjon forfatter () return $ this-> belongsTo ('User', 'author_id'); / ** * Gjør menyen med cachen * * @return-streng Html for sidelinker. * / offentlig statisk funksjon renderMenu () $ sider = Cache :: rememberForever ('pages_for_menu', funksjon () Return Page :: velg (array ('title', 'slug')) -> get () -> toArray ();); $ result = "; foreach ($ sider som $ side) $ result. = HTML :: action ('PagesController @ show', $ side ['title'], ['slug' => $ side ['slug'] ]). ' | '; returnere $ resultat; / ** * Glem cache når det er lagret * / offentlig funksjon afterSave ($ suksess) hvis ($ suksess) Cache :: glem (' pages_for_menu '); / ** * Glem cache når slettet * / offentlig funksjon slett () foreldre :: slett (); cache :: glem ('sider_for_meny');Vi kan observere den statiske metoden,
renderMenu ()
, Gir en rekke lenker for alle eksisterende sider. Denne verdien lagres i hurtigbuffertasten,'Pages_for_menu'
. På denne måten, i fremtiden, ringer tilrenderMenu ()
, det vil ikke være behov for å slå den virkelige databasen. Dette kan gi betydelige forbedringer i programmets ytelse.Men hvis a
Side
er lagret eller slettet (afterSave ()
ogdelete ()
metoder), vil verdien av cachen bli slettet, forårsakerrenderMenu ()
å gjenspeile den nye tilstanden til databasen. Så, hvis navnet på en side er endret, eller hvis den er slettet, vilnøkkel 'pages_for_menu'
blir slettet fra hurtigbufferen. (Cache :: glemme ( 'pages_for_menu');
)MERK: Metoden,
afterSave ()
, er tilgjengelig gjennom Ardent-pakken. Ellers ville det være nødvendig å implementerelagre()
Metode for å rense hurtigbufferen og ringeforelder :: spare ()
;Side tester
I:
app / prøver / modeller / PageTest.php
, vi skriver følgende tester:assertEquals ($ side-> author_id, $ page-> author-> id);Igjen har vi en "valgfri" test for å bekrefte forholdet. Som relasjoner er ansvaret for
Belyse \ Database \ Veltalende
, som allerede er dekket av Laravel sine egne tester, trenger vi ikke skrive en ny test for å bekrefte at denne koden fungerer som forventet.offentlig funksjon test_render_menu () $ sider = array (); for ($ i = 0; $ i < 4; $i++) $pages[] = FactoryMuff::create('Page'); $result = Page::renderMenu(); foreach ($pages as $page) // Check if each page slug(url) is present in the menu rendered. $this->assertGreaterThan (0, strpos ($ result, $ page-> slug)); // Sjekk om cachen er skrevet $ this-> assertNotNull (Cache :: get ('pages_for_menu'));Dette er en av de viktigste tester for
Side
modell. Først er det laget fire sider itil
sløyfe. Etter det, resultatet avrenderMenu ()
samtale er lagret i$ resultat
variabel. Denne variabelen skal inneholde en HTML-streng, som inneholder lenker til eksisterende sider.De
for hver
loop sjekker om sluggen (url) av hver side er til stede i$ resultat
. Dette er nok, siden det eksakte formatet til HTML ikke er relevant for våre behov.Til slutt bestemmer vi om cachenøkkelen,
pages_for_menu
, har noe lagret Med andre ord, gjorderenderMenu ()
samtalen lagret faktisk verdi til hurtigbufferen?offentlig funksjon test_clear_cache_after_save () // En testverdi lagres i cache Cache :: put ('pages_for_menu', 'avalue', 5); // Dette bør rense verdien i cachen $ page = FactoryMuff :: create ('Page'); $ Dette-> assertNull (Cache :: får ( 'pages_for_menu'));Denne testen tar sikte på å verifisere om, når du lagrer en ny
Side
, hurtigknappen'Pages_for_menu'
tømmes. DeFactoryMuff :: lage ( 'side');
til slutt utløserlagre()
metode, slik at det skulle være nok for nøkkelen,'Pages_for_menu'
, å bli ryddet.offentlig funksjon test_clear_cache_after_delete () $ page = FactoryMuff :: create ('Page'); // En testverdi lagres i hurtigbufferen Cache: put ('pages_for_menu', 'value', 5); // Dette bør rengjøre verdien i cachen $ side-> slette (); $ Dette-> assertNull (Cache :: får ( 'pages_for_menu'));I likhet med den forrige testen, bestemmer denne om nøkkelen
'Pages_for_menu'
tømmes riktig etter at du har slettet enSide
.Din
PageTest.php
skal se slik ut:assertEquals ($ side-> author_id, $ page-> author-> id); offentlig funksjon test_render_menu () $ sider = array (); for ($ i = 0; $ i < 4; $i++) $pages[] = FactoryMuff::create('Page'); $result = Page::renderMenu(); foreach ($pages as $page) // Check if each page slug(url) is present in the menu rendered. $this->assertGreaterThan (0, strpos ($ result, $ page-> slug)); // Sjekk om cachen er skrevet $ this-> assertNotNull (Cache :: get ('pages_for_menu')); offentlig funksjon test_clear_cache_after_save () // En testverdi lagres i cache Cache :: put ('pages_for_menu', 'avalue', 5); // Dette bør rense verdien i cachen $ page = FactoryMuff :: create ('Page'); $ Dette-> assertNull (Cache :: får ( 'pages_for_menu')); offentlig funksjon test_clear_cache_after_delete () $ page = FactoryMuff :: create ('Page'); // En testverdi lagres i hurtigbufferen Cache: put ('pages_for_menu', 'value', 5); // Dette bør rengjøre verdien i cachen $ side-> slette (); $ Dette-> assertNull (Cache :: får ( 'pages_for_menu'));Brukermodell
I tilknytning til tidligere presenterte modeller har vi nå
Bruker
. Her er koden for den modellen:'string', 'email' => 'email', 'password' => '123123', 'password_confirmation' => '123123',); / ** * Har mange sider * / offentlige funksjonssidene () return $ this-> hasMany ('Side', 'author_id'); / ** * Har mange innlegg * / offentlige funksjonstjenester () return $ this-> hasMany ('Post', 'author_id');Denne modellen er fraværende fra tester.
Vi kan observere det, med unntak av relasjoner (som kan være nyttig å teste), er det ingen metodeimplementering her. Hva med autentisering? Vel, bruken av Confide-pakken gir allerede implementering og tester for dette.
Tester for
Zizaco \ betro \ ConfideUser
er lokalisert i ConfideUserTest.php.Det er viktig å avgjøre klasseansvar før du skriver tester. Tester alternativet til "tilbakestill passordet" av a
Bruker
ville være overflødig. Dette skyldes at det riktige ansvaret for denne testen er innenforZizaco \ betro \ ConfideUser
; ikke iBruker
.Det samme gjelder for data valideringstester. Ettersom pakken, Ardent, håndterer dette ansvaret, ville det ikke gi stor mening å teste funksjonaliteten igjen.
Kort oppsummert: Hold tester rene og organisert. Bestem det riktige ansvaret for hver klasse, og test bare hva som er strengt ansvaret.
Konklusjon
Bruken av en in-memory database er en god praksis å utføre tester mot en database raskt. Takket være hjelp fra noen pakker, som Ardent, FactoryMuff og Confide, kan du minimere mengden kode i modellene dine, samtidig som testene blir rene og objektive.
I oppfølgeren til denne artikkelen vil vi se gjennom Controller testing. Følg med!
Fortsatt å komme i gang med Laravel 4, la oss lære deg det viktigste!