2011-02-26

TDD utflykt i PHP land, del 7

Skillnaden mellan att skapa en ny punkt och redigera en befintlig punkt är minimal. I alla fall ur användarperspektiv. Implementationsmässigt skiljer det sig en del.

Första frågan är, hur vet vi om det handlar om att lägga till en ny punkt eller redigera en befintlig? Svaret är att den inkommande URL:en skiljer sig åt. När vi redigerar en punkt talar vi om vilken det gäller genom att ange dess ID. I teorin räcker det med att ange punktens ID, men eftersom vi ändå behöver cachens ID (vi måste fortfarande verifiera att användaren får modifiera punkten) så känns det enklast om även cachens ID kommer via URL:en.

Att verifiera att punkten faktiskt tillhör cachen känns som ett enkelt testfall. Men ett ännu enklare testfall är att verifiera att felsidan visas om punkten inte finns.

function testSetsErrorIfWaypointDoesNotExist() { $cacheManager = new MockCache_Manager(); $childWpHandler = new MockChildWp_Handler(); $this->request->setForValidation(ChildWp_Presenter::req_cache_id, '345'); $this->request->setForValidation(ChildWp_Presenter::req_child_id, '456'); $cacheManager->setReturnValue('exists', true); $cacheManager->expectOnce('exists', array('345')); $cacheManager->setReturnValue('userMayModify', true); $cacheManager->expectOnce('userMayModify', array('345')); $childWpHandler->expectOnce('exists', array('456')); $childWpHandler->setReturnValue('exists', false); $presenter = $this->createPresenter(); $presenter->init($this, $cacheManager, $childWpHandler); $this->assertEqual(ERROR_CACHE_NOT_EXISTS, $this->errorCode); }

Det första som slår mig är att det behövs mycket kod för att sätta upp testfallet. Men innan jag kan göra något åt det så måste init() modifieras så att testfallet blir godkänt.

2011-02-13

TDD utflykt i PHP land, del 6

Det tillfälle som kanske är mest spännande när man TDD:ar fram en MVP-lösning (Model-View-Presenter) är när det är dags att koppla in en riktig vy för första gången. Arbetsgången är att skapa ett GUI-element (t. ex. en dialog eller UserControl) och implementera det gränssnitt som vyn ska ha. Därefter kopplar man ihop modellen, vyn och presentatören. Om man har gjort ett bra jobb under TDD-fasen så fungerar det nästan felfritt på en gång.

I vårt fall är vyn ett Smarty-objekt med tillhörande mallfil. Gränssnittet består till största del av ett antal variabler. Vi behöver också den PHP-sida som användaren navigerar till när denna vill lägga till en punkt. Det är sidan som ansvarar för att koppla ihop de ingående delarna.

Vi börjar med koden för att visa sidan, sedan tar vi hand om återpostning av sidan.

<?php $require('common.inc.php') // Innehåller bra-att-ha-saker (t. ex. koppling till databasen) $tpl->name = 'childwp'; // $tpl är ett Smarty-objekt deklarerat i common.inc.php $presenter = new ChildWp_Presenter(); $presenter->setTypes(array(new ChildWp_Type(1, 'Parking'), new ChildWp_Type(2, 'Reference point'))); $presenter->prepare($tpl); $tpl->display(); ?>

Som synes är punktens typ hårdkodat i php-filen. Det är inte en optimal lösning, men än så länge förekommer typerna bara på ett ställe så därför gör det inget. Eftersom det är mallens jobb att beskriva sidan, är så här lite kod väntat. Dags att lägga till lite mer logik. Vi har tidigare gjort en init()-metod som kontrollerar att cachen finns och att användaren får lägga till punkter till den.

2011-02-11

TDD utflykt i PHP land, del 5

Jag tror största problemet med typen på punkt är själva konfigurationsaspekten. Hur konfigureras typerna och var finns de sedan lagrade? Jag har ingen aning, så jag gör det som är enklast. Någon talar helt enkelt om för presentatören vilka typer som finns. Typen definieras som en vanlig värdeklass med åtminstone ID och namn.

function testSetWaypointTypeIds() { $waypointTypes = array(new ChildWp_Type(1, 'Type 1'), new ChildWp_Type(2, 'Type 2')); $presenter = new ChildWp_Presenter(); $presenter->setTypes($waypointTypes); $presenter->prepare($this); $this->assertTrue(in_array(1, $this->values['wpTypeIds'])); $this->assertTrue(in_array(2, $this->values['wpTypeIds'])); }

Det första testfallet är enkelt, bara kontrollerar att vektorn $wpTypeIds innehåller ID:na i godtycklig ordning. Lägg märke till att testklassen utgör mallen (istället för den tidigare mockversionen av mallen).

2011-02-09

TDD utflykt i PHP land, del 4

Förra inlägget avslutades med att lite "fusk" för att hantera koordinaten. Nu ska det bli ändring på det. Efter att ha funderat lite så har jag bestämt mig för att prova att återanvända den presentatör och visare jag redan gjort för koordinat. Skillnaden blir att själva HTML:en görs som ett tillägg till Smarty.

Första steget blir att modifiera namnen på variablerna i Request-objektet. Därefter borde det vara möjligt för ChildWp_Presenter att skapa en Coordinate_Presenter och delegera till den. Det modifierade testfallet ser ut så här.

function testChildWpIsAdded() { $request = new Http_Request(); $childWpHandler = new MockChildWp_Handler(); $request->set('wp_type', 1); $request->set(Coordinate_Presenter::lat_hem, 'N'); $request->set(Coordinate_Presenter::lat_deg, '10'); $request->set(Coordinate_Presenter::lat_min, '15'); $request->set(Coordinate_Presenter::lon_hem, 'E'); $request->set(Coordinate_Presenter::lon_deg, '20'); $request->set(Coordinate_Presenter::lon_min, '30'); $request->set('desc', 'my waypoint'); $childWpHandler->expectOnce('add', array(1, 10.25, 20.5, 'my waypoint')); $presenter = new ChildWp_Presenter($request); $presenter->addWaypoint($childWpHandler); }

Coordinate_Presenter skapas i konstruktorn. Förändringen i addWaypoint() blir liten.

public function addWaypoint($childWpHandler) { $coordinate = $this->coordinate->getCoordinate(); $childWpHandler->add($this->getType(), $coordinate->latitude(), $coordinate->longitude(), $this->getDesc()); }

Att få in koordinaten var enkelt. Däremot är det fortfarande mer komplicerat att sätta upp förutsättningarna i testfallet. Istället för att ange de enskilda komponenterna i en koordinat så hade jag hellre gjort det på ett mer abstrakt sätt. Det här är typiskt en sådan sak man skriver upp på att-göra-listan, men låter vara för tillfället.

2011-02-06

TDD utflykt i PHP land, del 3

Det har blivit anledning att göra en ny utflykt. Den här gången handlar det om att lägga till funktionen extra punkter. En cache kan ha ett godtyckligt antal extra punkter knutna till sig. Det påminner lite om det som gjorts tidigare då inmatning av en koordinat är centralt även här. Skillnaden är att det ska göras på en egen sida. Dessutom kommer sidan göras med Smarty.


Det enklaste sättet att göra något sådant här, särskilt om man inte gjort det tidigare, är att ta en befintlig sida (en php-fil och en mallfil) som gör något liknande och modifiera den tills det att man har fått det man vill. Jag började med det också, mest för att få mall-filen på plats. Jag ignorerade logiken i php-filen och ersatte variablerna med hårdkodade värden.