In het tweede deel van deze serie genaamd Laravel, BDD en You, zullen we onze eerste feature beginnen te beschrijven en bouwen met behulp van Behat en PhpSpec. In het laatste artikel hebben we alles voorbereid en zagen we hoe gemakkelijk we met Laravel kunnen communiceren in onze Behat-scenario's.
Onlangs schreef de maker van Behat, Konstantin Kudryashov (ook bekend als everzet) een geweldig artikel met de naam Introducing Modeling by Example. De workflow die we gaan gebruiken wanneer we onze functie bouwen, is in hoge mate geïnspireerd op de workflow van everzet.
Kortom, we gaan hetzelfde gebruiken .voorzien zijn van
om zowel ons kerndomein als onze gebruikersinterface te ontwerpen. Ik heb vaak het gevoel gehad dat ik veel duplicatie in mijn functies had in mijn acceptatie / functionele en integratie-suites. Toen ik Everzet's suggestie las over het gebruik van dezelfde functie voor meerdere contexten, klikte het allemaal voor mij en ik geloof dat het de manier is om te gaan.
In ons geval zullen we onze functionele context hebben, die voorlopig ook zal dienen als onze acceptatielaag en onze integratiecontext, die ons domein zal bestrijken. We zullen beginnen met het bouwen van het domein en daarna de UI en framework-specifieke dingen achteraf toevoegen.
Om de benadering "gedeelde functie, meerdere contexten" te gebruiken, moeten we een aantal aanpassingen doen aan onze bestaande opstelling.
Ten eerste gaan we de welkomstfunctie verwijderen die we in het eerste deel hebben gedaan, omdat we het niet echt nodig hebben en het niet echt de generieke stijl volgt die we nodig hebben om meerdere contexten te gebruiken.
$ git rm functies / functioneel / welcome.feature
Ten tweede zullen we onze functies in de root van de Kenmerken
map, dus we kunnen doorgaan en de pad
attribuut van onze behat.yml
het dossier. We gaan ook de naam wijzigen LaravelFeatureContext
naar FunctionalFeatureContext
(vergeet niet om ook de klassennaam te wijzigen):
standaard: suites: functioneel: contexten: [FunctionalFeatureContext]
Tot slot, alleen al om de zaken een beetje op te ruimen, denk ik dat we alle Laravel-gerelateerde dingen in een eigen trekje moeten verwerken:
# features / bootstrap / LaravelTrait.php app) $ this-> refreshApplication (); / ** * Creëert de applicatie. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / public function createApplication () $ unitTesting = true; $ testEnvironment = 'testen'; return vereist __DIR __. '/ ... / ... /bootstrap/start.php';
In de FunctionalFeatureContext
we kunnen dan de eigenschap gebruiken en de dingen verwijderen die we net hebben verplaatst:
/ ** * Behat contextklasse. * / class FunctionalFeatureContext implementeert SnippetAcceptingContext gebruik LaravelTrait; / ** * Initialiseert context. * * Elk scenario krijgt een eigen contextobject. * Je kunt willekeurige argumenten ook aan behat.yml doorgeven aan de contextconstructor. * / public function __construct ()
Eigenschappen zijn een geweldige manier om je contexten op te ruimen.
Zoals gepresenteerd in deel één, gaan we een kleine applicatie bouwen voor tijdregistratie. De eerste functie gaat over het bijhouden van de tijd en het genereren van een urenstaat van de getraceerde items. Hier is de functie:
Feature: Tracking tijd Om de tijd besteed aan taken bij te houden Als een werknemer moet ik een tijdschema met tijdinvoeren beheren Scenario: het genereren van tijdschema Gezien ik heb de volgende tijdinvoeringen | taak | duur | | codering | 90 | | codering | 30 | | documenteren | 150 | Wanneer ik de urenstaat genereer, moet mijn totale tijd besteed aan codering 120 minuten zijn. Mijn totale tijd besteed aan het documenteren moet 150 minuten zijn. Mijn totale tijd besteed aan vergaderingen moet 0 minuten zijn. Mijn totale tijdsbesteding zou 270 minuten moeten zijn.
Vergeet niet dat dit slechts een voorbeeld is. Ik vind het eenvoudiger om functies in het echte leven te definiëren, omdat je een echt probleem hebt dat je moet oplossen en vaak de kans krijgt om de functie met collega's, klanten of andere belanghebbenden te bespreken.
Oké, laten we de Behat-stappen voor ons genereren:
$ vendor / bin / behat --dry-run --append-snippets
We moeten de gegenereerde stappen slechts een klein beetje aanpassen. We hebben slechts vier stappen nodig om het scenario te dekken. Het eindresultaat zou er ongeveer zo uit moeten zien:
/ ** * @Given Ik heb de volgende tijdinvoeringen * / public function iHaveTheFollowingTimeEnies (TableNode $ table) nieuwe PendingException (); / ** * @Wanneer ik het tijdoverzicht * / public function genereer iGenerateTheTimeSheet () gooi nieuwe PendingException (); / ** * @Then mijn totale tijd besteed aan: taak moet zijn: expectedDuration minutes * / public function myTotalTimeSpentOnTaskShouldBeMinutes ($ task, $ expectedDuration) gooi nieuwe PendingException (); / ** * @Dat mijn totale tijd zou moeten zijn: verwachtDuration minutes * / public function myTotalTimeSpentShouldBeMinutes ($ expectedDuration) gooi nieuwe PendingException ();
Onze functionele context is nu helemaal klaar, maar we hebben ook een context nodig voor onze integratiesuite. Eerst voegen we de suite toe aan de behat.yml
het dossier:
standaard: suites: functioneel: contexten: [FunctionalFeatureContext] -integratie: contexten: [IntegrationFeatureContext]
Vervolgens kunnen we de standaard kopiëren FeatureContext
:
$ cp features / bootstrap / FeatureContext.php features / bootstrap / IntegrationFeatureContext.php
Vergeet niet de klassenaam te wijzigen in IntegrationFeatureContext
en ook om de gebruiksverklaring voor de te kopiëren PendingException
.
Ten slotte kunnen we, omdat we de functie delen, de vierstapsdefinities gewoon van de functionele context kopiëren. Als u Behat uitvoert, ziet u dat de functie tweemaal wordt uitgevoerd: eenmaal voor elke context.
Op dit moment zijn we klaar om de in behandeling zijnde stappen in onze integratiecontext in te vullen om het kerndomein van onze applicatie te ontwerpen. De eerste stap is Gezien ik de volgende tijden heb
, gevolgd door een tabel met tijdsinvoerrecords. Houd het simpel, laat ons gewoon de rijen van de tabel omlopen, probeer een tijdsinvoer voor elk van hen te maken, en voeg ze toe aan een ingangenreeks in de context:
gebruik TimeTracker \ TimeEntry; ... / ** * @Given Ik heb de volgende tijdinvoeringen * / public function iHaveTheFollowingTimeEnies (TableNode $ table) $ this-> entries = []; $ rows = $ table-> getHash (); foreach ($ rijen als $ rij) $ entry = new TimeEntry; $ entry-> task = $ row ['task']; $ entry-> duration = $ row ['duration']; $ this-> entries [] = $ entry;
Running Behat zal een fatale fout veroorzaken, aangezien de TimeTracker \ TimeEntry
klasse bestaat nog niet. Dit is waar PhpSpec het podium betreedt. Uiteindelijk, TimeEntry
gaat een Weldadige klas worden, ook al maken we ons daar nog geen zorgen over. PhpSpec en ORM's zoals Eloquent spelen niet zo goed samen, maar we kunnen PhpSpec nog steeds gebruiken om de klasse te genereren en zelfs wat basisgedrag te specificeren. Laten we de PhpSpec-generators gebruiken om de TimeEntry
klasse:
$ vendor / bin / phpspec desc "TimeTracker \ TimeEntry" $ vendor / bin / phpspec run Wilt u dat ik 'TimeTracker \ TimeEntry' voor u maak? Y
Nadat de klasse is gegenereerd, moeten we de autoload-sectie van onze updaten composer.json
het dossier:
"autoload": "classmap": ["app / opdrachten", "app / controllers", "app / modellen", "app / database / migraties", "app / database / seeds"], "psr-4" : "TimeTracker \\": "src / TimeTracker",
En natuurlijk rennen componist dump-autoload
.
Hardlopen PhpSpec geeft ons groen. Running Behat geeft ons ook groen. Wat een geweldige start!
Laat Be Behat ons een weg wijzen, wat dacht je van, we gaan gewoon door naar de volgende stap, Wanneer ik de urenstaat genereer
, meteen?
Het sleutelwoord hier is "genereren", wat lijkt op een term uit ons domein. In de wereld van een programmeur kan het vertalen van "het timesheet genereren" naar code gewoon betekenen dat een a Rooster
klas met een hoop tijdinvoeren. Het is belangrijk om te proberen vast te houden aan de taal uit het domein wanneer we onze code ontwerpen. Op die manier kan onze code helpen bij het beschrijven van het beoogde gedrag van onze applicatie.
Ik identificeer de term voortbrengen
zo belangrijk voor het domein, en daarom denk ik dat we een statische methode voor genereren moeten hebben op a Rooster
klasse die dient als een alias voor de constructor. Deze methode moet een verzameling tijdinvoeren bevatten en deze op het urenformulier opslaan.
In plaats van alleen een array te gebruiken, denk ik dat het logisch is om het te gebruiken Verlichten \ Support \ Collection
klas die met Laravel komt. Sinds TimeEntry
zal een Eloquent-model zijn, wanneer we de database doorzoeken voor tijdinvoer, krijgen we hoe dan ook een van deze Laravel-collecties. Wat dacht je van zoiets als dit:
gebruik Illuminate \ Support \ Collection; gebruik TimeTracker \ TimeSheet; gebruik TimeTracker \ TimeEntry; ... / ** * @Wanneer ik het urenformulier genereer * / public function iGenerateTheTimeSheet () $ this-> sheet = TimeSheet :: generate (Collection :: make ($ this-> entries));
Overigens, TimeSheet zal geen echte klasse worden. Tenminste voorlopig hoeven we alleen maar de tijdinvoer aan te houden en dan zijn de urenstaten gewoon gegenereerd uit de inzendingen.
Het uitvoeren van Behat zal opnieuw een fatale fout veroorzaken, omdat Rooster
bestaat niet. PhpSpec kan ons helpen dat op te lossen:
$ vendor / bin / phpspec desc "TimeTracker \ TimeSheet" $ vendor / bin / phpspec run Wilt u dat ik 'TimeTracker \ TimeSheet' voor u maak? y $ leverancier / bin / phpspec uitvoeren $ vendor / bin / behat PHP Fatale fout: aanroep op niet-gedefinieerde methode TimeTracker \ TimeSheet :: generate ()
We krijgen nog steeds een fatale fout na het maken van de klasse, omdat de statische genereren ()
methode bestaat nog steeds niet. Omdat dit een heel eenvoudige statische methode is, denk ik niet dat er een spec nodig is. Het is niets meer dan een verpakking voor de aannemer:
entries = $ entries; openbare statische functie genereren (Collection $ entries) return new static ($ entries);
Hierdoor wordt Behat weer groen, maar PhpSpec piept nu naar ons en zegt: Argument 1 doorgegeven aan TimeTracker \ TimeSheet :: __ construct () moet een instantie zijn van Illuminate \ Support \ Collection, geen gegeven
. We kunnen dit oplossen door een eenvoudig te schrijven laat()
functie die vóór elke specificatie wordt genoemd:
put (nieuwe TimeEntry); $ This-> beConstructedWith ($ data); function it_is_initializable () $ this-> shouldHaveType ('TimeTracker \ TimeSheet');
Dit zorgt ervoor dat we over de hele linie weer groen worden. De functie zorgt ervoor dat de urenstaat altijd wordt geconstrueerd met een mock van de Collection-klasse.
We kunnen nu veilig doorgaan naar de Toen mijn totale tijd besteed aan ...
stap. We hebben een methode nodig die een taaknaam overneemt en de geaccumuleerde duur van alle items met deze taaknaam retourneert. Rechtstreeks vertaald van augurk naar code, dit kan zoiets zijn totalTimeSpentOn ($ task)
:
/ ** * @Daarna mijn totale tijd besteed aan: taak moet zijn: verwachtDuurminuten * / openbare functie myTotalTimeSpentOnTaskShouldBeMinutes ($ task, $ expectedDuration) $ actualDuration = $ this-> sheet-> totalTimeSpentOn ($ task); PHPUnit :: assertEquals ($ expectedDuration, $ actualDuration);
De methode bestaat niet, dus het runnen van Behat zal ons geven Roep tot niet-gedefinieerde methode TimeTracker \ TimeSheet :: totalTimeSpentOn ()
.
Om de methode te specificeren, zullen we een specificatie schrijven die lijkt op de een of andere manier vergelijkbaar met wat we al in ons scenario hebben:
function it_should_calculate_total_time_spent_on_task () $ entry1 = new TimeEntry; $ entry1-> task = 'sleeping'; $ entry1-> duur = 120; $ entry2 = nieuwe TimeEntry; $ entry2-> task = 'eten'; $ entry2-> duur = 60; $ entry3 = nieuwe TimeEntry; $ entry3-> task = 'sleeping'; $ entry3-> duur = 120; $ collection = Collection :: make ([$ entry1, $ entry2, $ entry3]); $ This-> beConstructedWith ($ collectie); $ This-> totalTimeSpentOn ( 'slapen') -> shouldbe (240); $ This-> totalTimeSpentOn ( 'eten') -> shouldbe (60);
Merk op dat we geen spot gebruiken voor de TimeEntry
en Verzameling
instances. Dit is onze integratiesuite en ik denk niet dat we dit moeten bespotten. De objecten zijn vrij eenvoudig en we willen ervoor zorgen dat de objecten in ons domein interageren zoals we verwachten. Er zijn waarschijnlijk veel meningen hierover, maar dit is logisch voor mij.
Verhuizen:
$ vendor / bin / phpspec run Wilt u dat ik 'TimeTracker \ TimeSheet :: totalTimeSpentOn ()' voor u maak? y $ vendor / bin / phpspec run 25 ✘ het moet de totale tijd besteed aan de verwachte taak [integer: 240] berekenen, maar is null.
Om de vermeldingen te filteren, kunnen we de filter()
methode op de Verzameling
klasse. Een eenvoudige oplossing die ons groener maakt:
public function totalTimeSpentOn ($ task) $ entries = $ this-> entries-> filter (functie ($ entry) use ($ task) return $ entry-> task === $ task;); $ duur = 0; foreach ($ entries as $ entry) $ duration + = $ entry-> duration; return $ duration;
Onze specificatie is groen, maar ik denk dat we hier kunnen profiteren van wat refactoring. De methode lijkt twee verschillende dingen te doen: filteringangen en accumuleer de duur. Laten we de laatste naar zijn eigen methode uitpakken:
public function totalTimeSpentOn ($ task) $ entries = $ this-> entries-> filter (functie ($ entry) use ($ task) return $ entry-> task === $ task;); return $ this-> sumDuration ($ entries); beschermde functie sumDuration ($ entries) $ duration = 0; foreach ($ entries as $ entry) $ duration + = $ entry-> duration; return $ duration;
PhpSpec is nog steeds groen en we hebben nu drie groene stappen in Behat. De laatste stap moet eenvoudig te implementeren zijn, omdat deze enigszins lijkt op degene die we net hebben gedaan.
/ ** * @Daarna zou mijn totale tijd moeten zijn: verwachtDuurminuten * / openbare functie myTotalTimeSpentShouldBeMinutes ($ expectedDuration) $ actualDuration = $ this-> sheet-> totalTimeSpent (); PHPUnit :: assertEquals ($ expectedDuration, $ actualDuration);
Running Behat zal ons geven Roep tot niet-gedefinieerde methode TimeTracker \ TimeSheet :: totalTimeSpent ()
. In plaats van een afzonderlijk voorbeeld in onze spec voor deze methode te doen, hoe zit het dan met het gewoon toevoegen aan degene die we al hebben? Het is misschien niet helemaal in overeenstemming met wat "goed" is om te doen, maar laten we een beetje pragmatisch zijn:
... $ this-> beConstructedWith ($ collection); $ This-> totalTimeSpentOn ( 'slapen') -> shouldbe (240); $ This-> totalTimeSpentOn ( 'eten') -> shouldbe (60); $ This-> totalTimeSpent () -> shouldbe (300);
Laat PhpSpec de methode genereren:
$ vendor / bin / phpspec run Wilt u dat ik 'TimeTracker \ TimeSheet :: totalTimeSpent ()' voor u maak? y $ vendor / bin / phpspec run 25 ✘ het moet de totale tijd besteed aan de verwachte taak [integer: 300] berekenen, maar is null.
Naar groen gaan is gemakkelijk nu we de. Hebben sumDuration ()
methode:
public function totalTimeSpent () return $ this-> sumDuration ($ this-> entries);
En nu hebben we een groene functie. Ons domein evolueert langzaam!
Nu gaan we naar onze functionele suite. We gaan de gebruikersinterface ontwerpen en omgaan met alle Laravel-specifieke zaken die niet van belang zijn voor ons domein.
Tijdens het werken in de functionele suite kunnen we de -s
vlag om Behat op te dragen om onze functies alleen via de FunctionalFeatureContext
:
$ vendor / bin / behat -s functioneel
De eerste stap zal lijken op de eerste van de integratiecontext. In plaats van alleen de gegevens in een array op de context aan te houden, moeten we ze ook daadwerkelijk in een database bewaren zodat ze later kunnen worden opgehaald:
gebruik TimeTracker \ TimeEntry; ... / ** * @Given Ik heb de volgende tijdinvoeringen * / public function iHaveTheFollowingTimeEntries (TableNode $ table) $ rows = $ table-> getHash (); foreach ($ rijen als $ rij) $ entry = new TimeEntry; $ entry-> task = $ row ['task']; $ entry-> duration = $ row ['duration']; $ Entry-> save ();
Running Behat geeft ons een fatale fout Oproep tot ongedefinieerde methode TimeTracker \ TimeEntry :: save ()
, sinds TimeEntry
nog steeds is geen Welsprekend model. Dat is eenvoudig op te lossen:
naamruimte TimeTracker; class TimeEntry breidt \ Eloquent uit
Als we Behat opnieuw uitvoeren, klaagt Laravel dat het geen verbinding met de database kan maken. We kunnen dit oplossen door een database.php
bestand naar de app / config / testen
directory om de verbindingsdetails voor onze database toe te voegen. Voor grotere projecten wilt u waarschijnlijk dezelfde databaseserver gebruiken voor uw tests en uw productiecodebasis, maar in ons geval gebruiken we alleen een SQLite-database in het geheugen. Dit is super eenvoudig om in te stellen met Laravel:
'sqlite', 'verbindingen' => array ('sqlite' => array ('driver' => 'sqlite', 'database' => ': geheugen:', 'prefix' => ",),),) ;
Als we nu Behat gebruiken, zal het ons vertellen dat er geen is time_entries
tafel. Om dit te verhelpen, moeten we een migratie uitvoeren:
$ php artisan migrate: make createTimeEntriesTable --create = "time_entries"
Schema :: create ('time_entries', functie (Blueprint $ table) $ table-> increments ('id'); $ table-> string ('task'); $ table-> integer ('duration'); $ tabel-> tijdstempels (););
We zijn nog steeds niet groen, want we hebben een manier nodig om Behat opdracht te geven onze migraties vóór elk scenario uit te voeren, dus we hebben elke keer een schone lei. Door de annotaties van Behat te gebruiken, kunnen we deze twee methoden toevoegen aan de LaravelTrait
eigenschap:
/ ** * @BeforeScenario * / openbare functie setupDatabase () $ this-> app ['artisan'] -> call ('migrate'); / ** * @AfterScenario * / public function cleanDatabase () $ this-> app ['artisan'] -> call ('migrate: reset');
Dit is behoorlijk netjes en krijgt onze eerste stap naar groen.
De volgende is de Wanneer ik de urenstaat genereer
stap. De manier waarop ik het zie, het genereren van de urenstaat is het equivalent van het bezoeken van de inhoudsopgave
actie van de tijdsinvoerbron, aangezien het tijdschema de verzameling van alle tijdinvoeren is. Dus het tijdschema-object is als een container voor alle tijdinvoeren en geeft ons een leuke manier om gegevens te verwerken. In plaats van naar toe te gaan / Tijd-entries
, om de urenstaat te zien, denk ik dat de werknemer zou moeten gaan /rooster
. We zouden dat in onze stap-definitie moeten plaatsen:
/ ** * @Wanneer ik de urenstaat * / public function genereer iGenerateTheTimeSheet () $ this-> call ('GET', '/ time-sheet'); $ this-> crawler = new Crawler ($ this-> client-> getResponse () -> getContent (), url ('/'));
Dit veroorzaakt een NotFoundHttpException
, omdat de route nog niet is gedefinieerd. Zoals ik net heb uitgelegd, denk ik dat deze URL moet verwijzen naar de inhoudsopgave
actie op de tijdsinvoerbron:
Route :: get ('time-sheet', ['as' => 'time_sheet', 'uses' => 'TimeEntriesController @ index']);
Om groen te worden, moeten we de controller genereren:
$ php artisan controller: maak TimeEntriesController $ composer dump-autoload
En daar gaan we.
Ten slotte moeten we de pagina crawlen om de totale duur van de tijdsinvoer te vinden. Ik denk dat we een soort tafel zullen hebben die de duur samenvat. De laatste twee stappen zijn zo vergelijkbaar dat we ze net op hetzelfde moment gaan implementeren:
/ ** * @Daarna mijn totale tijd besteed aan: taak moet zijn: expectedDuration minutes * / public function myTotalTimeSpentOnTaskShouldBeMinutes ($ task, $ expectedDuration) $ actualDuration = $ this-> crawler-> filter ('td #'. $ Task . 'TotalDuration') -> text (); PHPUnit :: assertEquals ($ expectedDuration, $ actualDuration); / ** * @Daar mijn totale tijd zou moeten zijn: verwachtDuurminuten * / publieke functie myTotalTimeSpentShouldBeMinutes ($ expectedDuration) $ actualDuration = $ this-> crawler-> filter ('td # totalDuration') -> text (); PHPUnit :: assertEquals ($ expectedDuration, $ actualDuration);
De crawler is op zoek naar een Omdat we nog steeds geen mening hebben, zal de crawler ons dat vertellen Om dit op te lossen, laten we het bouwen De weergave bestaat vooralsnog uit een eenvoudige tabel met de samengevatte duurwaarden: Als u Behat opnieuw uitvoert, ziet u dat we de functie met succes hebben geïmplementeerd. Misschien moeten we even de tijd nemen om ons te realiseren dat we niet eens een browser openden! Dit is een enorme verbetering van onze workflow, en als een leuke bonus hebben we nu geautomatiseerde tests voor onze applicatie. Yay! Als je rent Als u deze twee opdrachten uitvoert: Je zult zien dat we terug groen zijn, zowel met Behat en PhpSpec. Yay! We hebben nu onze eerste functie beschreven en ontworpen, volledig gebruikmakend van een BDD-benadering. We hebben gezien hoe we kunnen profiteren van het ontwerpen van het kerndomein van onze applicatie, voordat we ons zorgen maken over de gebruikersinterface en de framework-specifieke dingen. We hebben ook gezien hoe gemakkelijk het is om te communiceren met Laravel, en met name de database, in onze Behat-contexten. In het volgende artikel gaan we veel refactoring doen om te veel logica te vermijden in onze Eloquent-modellen, omdat deze moeilijker te testen zijn in een isolement en nauw verbonden zijn met Laravel. Blijf kijken! knoop met een id van [Naam_taak] TotalDuration
of totale duur
in het laatste voorbeeld.De huidige knooppuntenlijst is leeg.
inhoudsopgave
actie. Eerst halen we de verzameling tijdinvoer op. Ten tweede genereren we een urenstaat van de ingangen en sturen deze mee naar de (nog steeds niet bestaande) weergave.gebruik TimeTracker \ TimeSheet; gebruik TimeTracker \ TimeEntry; class TimeEntriesController breidt \ BaseController / ** uit * Geef een lijst van de resource weer. * * @return Response * / public function index () $ entries = TimeEntry :: all (); $ sheet = TimeSheet :: generate ($ entries); return View :: make ('time_entries.index', compact ('sheet')); ...
Urenstaat
Taak Totale duur codering $ sheet-> totalTimeSpentOn ('coding') documenteren $ sheet-> totalTimeSpentOn ('documenting') vergaderingen $ sheet-> totalTimeSpentOn ('meetings') Totaal $ sheet-> totalTimeSpent () Conclusie
verkoper / bin / Behat
om beide Behat-suites te gebruiken, zult u zien dat ze beide nu groen zijn. Als u echter PhpSpec uitvoert, ziet u helaas dat onze specificaties gebroken zijn. We krijgen een fatale fout Klasse 'Welsprekend' niet gevonden in ...
. Dit komt omdat Welsprekendheid een alias is. Als je even binnenkijkt app / config / app.php
onder aliassen, zul je dat zien Welsprekend
is eigenlijk een alias voor Verlichten \ Database \ Welsprekend \ Model
. Om PhpSpec weer groen te maken, moeten we deze klasse importeren:naamruimte TimeTracker; gebruik Illuminate \ Database \ Eloquent \ Model als Weldadig; les TimeEntry breidt welsprekendheid uit
$ leverancier / bin / phpspec run; verkoper / bin / Behat