BDD JavaScript-kielellä Kurkku

0

Todd Anderson

olen aiemmin kirjoittanut Test driven developmentista (TDD) Javascriptissä, erityisesti käyttämällä behavior driven development (BDD)-tyylikirjastoa Jasmine-sarjassa Testiohjatun Päivittäistavaraluettelosovelluksen rakentamisesta. Tässä virkaa sarjassa kävin läpi ajattelu käyttäjien tarinoita ominaisuuksia ja skenaarioita todellisina kehitystehtäviä, ja – lukeminen takaisin sitä – se on kaikki hyvin vihreä (ei sanaleikki tarkoitettu) sikäli kuin Olen löytää tapa toimittaa testiohjattu koodi. Siinä ei ole mitään vikaa, ja aion todennäköisesti tarkastella tätä ja myöhempiä virkoja samalla tavalla. Se sanoi, Olen edelleen totta, että TDD on paras tapa toimittaa tiivis, testattu ja harkittu koodi.

siitä lähtien olen kuitenkin sisällyttänyt TDD-työnkulkuun toisen työkalun JavaScript-pohjaisiin projekteihin, joka tarjoaa minulle ominaisuustietojen integroinnin tiiviimmin omaan kehitystyöhöni ja todella kattaa nykyisen Käyttäytymismallini kehityksen ihanteen: CucumberJS. Pohjimmiltaan, se antaa minulle mahdollisuuden todella noudattaa TDD kehittäessään ulkopuolelta käynnissä automatisoituja testejä, jotka epäonnistuvat, kunnes olen kirjoittanut koodia, joka tukee ominaisuutta.

oletukset ja huomautukset

tämän julkaisun esimerkkien osalta oletetaan, että tunnet NodeJS: n, npm: n, Solmumoduulien kehittämisen ja yhteiset yksikkötestauskäytännöt, koska nämä aiheet ovat liian suuria keskusteltavaksi tässä viestissä.

tätä aihetta tukevia tiedostoja on saatavilla osoitteessa:
https://github.com/bustardcelly/cucumberjs-examples

Kurkku

kurkku on suositun BDD-työkalun kurkun JavaScript-portti (joka itsessään oli RSC: n uudelleenkirjoitus). Sen avulla voit määrittää ominaisuustiedot Verkkotunnuskohtaisella kielellä (DSL) – nimeltään Gherkin-ja ajaa tekniset tiedot käyttäen komentorivityökalua, joka raportoi skenaarioiden kulusta ja/tai epäonnistumisesta ja vaiheista, joista ne koostuvat.

on tärkeää huomata, että kurkku itsessään ei tarjoa oletuskirjastoa. Se on testauskehys tarjoaa komentorivityökalu, joka kuluttaa määriteltyjä ominaisuuksia ja validoi skenaarioita ajamalla vaiheet, jotka on kirjoitettu JavaScript. Se on kehittäjien valinta sisällyttää haluttu väitteen kirjasto, jota käytetään, jotta nämä vaiheet läpäisevät tai epäonnistuvat. Tarkoitukseni on selventää prosessia esimerkillä yhdellä ominaisuudella, jossa on useita skenaarioita tässä viestissä.

asennus

voit asentaa CucumberJS: n projektiisi npm: n avulla:

$ npm install cucumber --save-dev

Gherkin

jos olet seurannut mukana aiemmassa TDD-sarjassa, löydät siinä sarjassa määritellyt speksit samantapaisia kuin Gherkin. Itse asiassa, aion uudelleen hashing ominaisuus spec että sarja osoittaa läpi ensimmäinen cuke (aka, passing ominaisuus spec).

jos uudistaisimme TDD/BDD: n Päivittäistavaraluettelosovelluksen kurkun avulla, aloittaisimme ensin ominaisuudella, joka käyttää Gherkin-syntaksia:

/features/add-item.ominaisuus

Feature: Shopper can add an item to their Grocery List As a grocery shopper I want to add an item to my grocery list So that I can remember to buy that item at the grocery store Scenario: Item added to grocery list Given I have an empty grocery list When I add an item to the list Then The grocery list contains a single item Scenario: Item accessible from grocery list Given I have an empty grocery list When I add an item to the list Then I can access that item from the grocery list

ominaisuus määrittää liiketoiminnan arvon, kun taas skenaarioissa määritellään vaiheet, jotka antavat kyseisen arvon. Useimmiten ohjelmistokehitysmaailmassa juuri näistä skenaarioista otetaan kehitystehtäviä ja määritellään QA-testejä.

pysähdyin kahteen skenaarioon, mutta voisimme hyvin helposti lisätä tähän ominaisuuteen useampia skenaarioita; heti tulee mieleen kohteen lisäämissäännöt ja ominaisuuksien validointi, jotka mahdollistavat kohteen lisäämisen tai hylkäämisen. Jälkikäteen, se voisi olla järkevämpää luoda erillinen ominaisuus silmälasit tällaisia yksityiskohtia. Voisimme viettää koko postitse tällaisia aiheita kuitenkin, joten palataan ominaisuus jo määritelty.

kussakin skenaariossa on luettelo peräkkäisistä vaiheista: annettu, milloin ja sitten. Nämä vaiheet Kurkku suorittaa sen jälkeen, kun olet kuluttanut tämän ominaisuuden spec. Jokaisen näistä, voit valinnaisesti on Ja Ja mutta, kuitenkin – vaikka tarpeen ja väistämätöntä ajoittain – yritän pysyä pois tällaisia ylimääräisiä askel lausekkeita.

sen suorittaminen

Tallennettuamme sen tiedostoon / features-hakemistoon, voimme sitten suorittaa sen Cucumberin alla:

$ node_modules/.bin/cucumber-js

oletusarvoisesti Kurkku kuluttaa kaikki relative / features-hakemistosta löytyvät ominaisuustiedot.

nykyinen konsolin ulostulo näyttää suunnilleen seuraavanlaiselta, mikä tarkoittaa käytännössä sitä, että kaikkia vaiheita ei ole paikannettu tai määritelty:

UUUUUU2 scenarios (2 undefined)6 steps (6 undefined)You can implement step definitions for undefined steps with these snippets:this.Given(/^I have an empty grocery list$/, function(callback) { // express the regexp above with the code you wish you had callback.pending();});this.When(/^I add an item to the list$/, function(callback) { // express the regexp above with the code you wish you had callback.pending();});this.Then(/^The grocery list contains a single item$/, function(callback) { // express the regexp above with the code you wish you had callback.pending();});this.Then(/^I can access that item from the grocery list$/, function(callback) { // express the regexp above with the code you wish you had callback.pending();});

joten meillä on 6 määrittelemätöntä vaihetta, jotka muodostavat 2 skenaariota ja CucumberJS ci-työkalu tarjoaa jopa esimerkkejä niiden määrittelystä!

tärkeä osa tätä pätkää ymmärtää on, että on vain 4 vaihetta toteuttaa. Meidän ominaisuus meillä on 2 Scenerios kussakin 3 vaiheet. On yhteensä 6 vaihetta, mutta meidän tarvitsee vain määritellä 4. Syy on, että jokainen skenaario jakaa saman annettuna ja kun vaihe; nämä on vain määriteltävä kerran ja suoritetaan erikseen kunkin skenaarion. Pohjimmiltaan, jos määrität samanlaisia vaiheita käyttäen samassa yhteydessä, se käyttää uudelleen ”setup” yhden vaiheen kussakin skenaariossa.

käytän lainausmerkeissä sanaa ”setup”, koska tarkoitan sitä enemmän kontekstin määrittelyssä milloin ja sitten vaiheet.

en halua sekoittaa sitä muiden yksikkötestauskäytäntöjen setup/teardown-menetelmiin – jotka tunnetaan nimellä ennen / jälkeen-tukitehtävät Cucumberjsissä-ja pitää sisällään enemmän kontekstia sellaisen ympäristön perustamiselle, jossa testit sitten suoritetaan (kuten täyttämällä DB käyttäjiä) ja sitten purkamalla tämä asetus.

Step Definitions

edellisessä osiossa näimme, että kurkkujen käyttäminen Add Item-ominaisuutta vastaan hälytti meidät siitä, että meillä on määrittelemättömiä (ja, vaikka niitä ei ole painettu punaisella, epäonnistuneita) skenaarioita, jotka tukevat ominaisuutta. Oletuksena CucumberJS lukee kaikki ominaisuudet / ominaisuudet-hakemistosta suhteessa siihen, missä komento suoritettiin, mutta se ei löytänyt tuettuja step-tiedostoja, joissa nämä menetelmät on määritelty.

kuten aiemmin mainittiin, kurkku ei tarjoa väityskirjastoa. Ainoa oletus tässä vaiheessa – koska CucumberJS-työkalu suoritetaan nodejs-komennolla-on, että tuetut vaiheet Ladataan solmumoduuleina, joissa on viety toiminto, joka suoritetaan. Kun alamme toteuttaa vaiheita, meidän täytyy päättää väitteen kirjasto käyttää validointi logiikkamme. Laitamme päätöksen hyllylle ja saamme barebonesin lavastuksen epäonnistumaan.

aloitetaan ottamalla CucumberJS ci-työkalun tarjoamat askelmääritykset ja pudottamalla ne solmumoduuliin:

/ features/stepdefinitions / add-item.vaihe.Js_

'use strict';module.exports = function() { this.Given(/^I have an empty grocery list$/, function(callback) { callback.pending(); }); this.When(/^I add an item to the list$/, function(callback) { callback.pending(); }); this.Then(/^The grocery list contains a single item$/, function(callback) { callback.pending(); }); this.Then(/^I can access that item from the grocery list$/, function(callback) { callback.pending(); });};

oletusarvoisesti Kurkku etsii vaiheet, jotka ladataan kansioon step_definitions / features-hakemistossa suhteessa siihen, mihin komennon annat. Vaihtoehtoisesti voit käyttää -r – vaihtoehtoa, jos haluat, että kurkku on lastattu toiseen paikkaan. Oletusarvon suorittaminen on sama kuin seuraavan vaiheen määrityshakemisto-vaihtoehdon asettaminen:

./node_modules/.bin/cucumber-js -r features/step_definitions

konsolin ulostulo näyttää nyt seuraavilta:

P--P--2 scenarios (2 pending)6 steps (2 pending, 4 skipped)

ei liian yliampuvaa, koska ilmoitamme takaisinkutsun pending tilaan. Kurkku tulee ensimmäiseen vaiheeseen (annettu) ja palautetaan välittömästi odottavalla ilmoituksella. Sellaisenaan, se ei vaivaudu syöttämällä myöhempiä vaiheita ja merkitsee ne ohitettu.

huomaa: on liikaa lähteä keskustelemaan asiakaspuolen moduuleista ja AMD vs. CommonJS. Tässä esimerkissä aion käyttää CommonJS: ää, koska olen tällä hetkellä kiinnostunut Browserifyn hyödyntämisestä asiakaspuolen kehittämisessä. Pitkään olin Requejs: n ja AMD: n kannattaja. Tämäkin on aivan toinen keskustelu.

koska

päästäisiin lähemmäksi vihreää, tartutaan annettuun askeleeseen ensin:

/features/stepdefinitions/add-item.vaihe.js_

'use strict';var GroceryList = require(process.cwd() + '/script/model/grocery-list');module.exports = function() { var myList; this.Given(/^I have an empty grocery list$/, function(callback) { myList = GroceryList.create(); callback(); }); ...};

jos ajaisimme tuon uudestaan, saisimme heti poikkeuksen:

$ ./node_modules/.bin/cucumber-jsmodule.js:340 throw err; ^Error: Cannot find module './script/model/grocery-list' at Function.Module._resolveFilename (module.js:338:15) at Function.Module._load (module.js:280:25) at Module.require (module.js:364:17) at require (module.js:380:17) at Object.<anonymous> (/Users/toddanderson/Documents/workspace/custardbelly/cucumberjs-example/features/step_definitions/add-item.steps.js:3:19)

mikä on ymmärrettävää, koska emme ole määritelleet mitään muuta koodia, mutta tämä vaihe määritelmä moduuli ja yrittävät vaatia moduuli, joka ei ole olemassa. Pysyttäessä TDD: ssä tämä on hyvä asia – tiedämme miksi se epäonnistuu ja odotamme sitä – vetäisin hiuksiani pois, jos se ei tekisi poikkeusta!

jotta tämä menisi läpi, luodaan määritettyyn hakemistoon Solmumoduuli, joka vie objektin create menetelmällä:

/script/model / grocery-list.js

'use strict';module.exports = { create: function() { return Object.create(null); }};

olemme antaneet minimivaatimuksen, jotta saamme annetun askeleemme läpi. Huolehdimme yksityiskohdista, kun lähestymme loppuvaiheita.

suorita se uudelleen, ja kurkku tulee kunkin skenaarion milloin-vaiheeseen ja keskeyttää odottavan palautuksen vuoksi.

$ ./node_modules/.bin/cucumber-js.P-.P-2 scenarios (2 pending)6 steps (2 pending, 2 skipped, 2 passed)

kun

edellisessä osiossa, jotta annetut askeleet kulkisivat jokaisessa skenaariossa, toteutimme lähtökohdat tehdasmenetelmällä syntyneelle Päivittäistavaraluettelomallille, create, grocery-list – moduulista. En halua lähteä väittelemään objektin luomisesta, new operaattorista, luokista ja prototyypeistä – ainakaan tässä postauksessa-ja lähden siitä, että olet tuttu ja mukava (ainakin lukukoodissa) objektin kanssa.luo määritelty ECMAScript 5.

arvioitaessa milloin-vaihetta skenaarioiden osalta:

When I add an item to the list

…meidän täytyy tarjota tapa, jolla lisätä kohteen päivittäistavarakaupan luetteloon esimerkiksi luotu annetun-ja tehdä niin vähän koodia, jotta askel kulkee.

ensin määrittelemme odotuksemme Päivittäistavaraluettelon kokoonpanosta ja add allekirjoituksesta askelmäärityksissä:

/ features/stepdefinitions / add-item.vaihe.js_

...module.exports = function() { var myList, listItem = 'apple'; this.Given(/^I have an empty grocery list$/, function(callback) { myList = GroceryList.create(); callback(); }); this.When(/^I add an item to the list$/, function(callback) { myList.add(listItem); callback(); }); ...};

jos toistamme:

$ ./node_modules/.bin/cucumber-js.F-.F-(::) failed steps (::)TypeError: Object object has no method 'add'

OOO-Wee! Nyt päästiin asiaan. Isot, kirkkaanpunaiset F: T. 🙂

jotta tuo pääsisi taas kulkemaan, muokkaamme grocery-list mahdollisimman pienellä koodilla:

/script/model / grocery-list.js

'use strict';var groceryList = { add: function(item) { // }};module.exports = { create: function() { return Object.create(groceryList); }};

Juokse uudelleen, ja kurkku on edennyt silloisiin vaiheisiin, jotka ilmoittavat pending tilan.

$ ./node_modules/.bin/cucumber-js..P..P2 scenarios (2 pending)6 steps (2 pending, 4 passed)

sitten

etenimme askeltoteutustemme kautta ja olemme saavuttaneet vaiheen(t), jossa vahvistamme operaatioita ja ominaisuuksia, jotka todistavat, että skenaariomme antaa sille tarkoitetun arvon. Kuten aiemmin mainittiin, CucumberJS ei tarjoa väitteen kirjastoa. Oma mieltymykseni argumentaatiokirjastoissa on yhdistelmä Chai, Sinon ja Sinon-Chai, mutta tämän postauksen esimerkeistä käytän vain nodejs: n mukana tulevaa assert – moduulia. Kannustan tutustumaan muihin väittämäkirjastoihin ja jättämään lapun, jos sinulla on suosikki.

Huom: tästä osiosta tulee hieman raskas esimerkki, kun vaihdamme nopeasti koodimme muokkaamisesta ja juoksemme spec Runneria usein.

ensimmäinen skenaario

arvioitaessa ensimmäisen skenaarion silloista vaihetta:

Then The grocery list contains a single item

…meidän täytyy todistaa, että päivittäistavarakaupan luettelo esimerkiksi kasvaa kertoimella 1 jokaista uutta tuotetta lisätään.

Päivitä vaihe ja määrittele, miten odotamme kyseisen eritelmän vahvistuvan:

/ feature/stepdefinitions / add-item.vaihe.js_

...var assert = require('assert');...module.exports = function() {... this.Then(/^The grocery list contains a single item$/, function(callback) { assert.equal(myList.getAll().length, 1, 'Grocery List should grow by one item.'); callback(); });...};...

olemme vetäneet assert moduulin sisään ja yrittäneet vahvistaa, että Päivittäistavaraluettelon pituus on kasvanut arvolla 1 sen jälkeen, kun olemme suorittaneet edellisen vaiheen – kun – lisätään tuote.

Juokse tuo, niin saamme poikkeuksen:

$ ./node_modules/.bin/cucumber-js..F..P(::) failed steps (::)TypeError: Object #<Object> has no method 'getAll'

lisätään tämä menetelmä ruokakauppojen luetteloon malli:

/script/model / grocery-list.js

'use strict';var groceryList = { add: function(item) { // }, getAll: function() { // }};module.exports = { create: function() { return Object.create(groceryList); }};

And back to running our specs:

$ ./node_modules/.bin/cucumber-js..F..P(::) failed steps (::)TypeError: Cannot read property 'length' of undefined

koska koodi ei palauta mitään getAll(): stä, emme voi käyttää length: n ominaisuutta väitöstestiimme.

jos muokkaamme koodia palataksemme Array:

/ feature/stepdefinitions / add-item.vaihe.js_

...getAll: function() { return ;}...

and run the specs again, we ’ ll get the assertion error message we provided:

$ ./node_modules/.bin/cucumber-js..F..P(::) failed steps (::)AssertionError: Grocery List should grow by one item.

nyt meille on ilmoitettu oikea epäonnistuminen väitteestä, joka aiheuttaa sen, että askel ei mene läpi. Hurraa!

pidä hengähdystauko

pysähdytään tässä hetkeksi ennen kuin lisätään lisää koodia, jotta tämä vaihe menee läpi. Kyseessä ei ole varsinaisesti palattavan kohteen lisääminen, vaan pikemminkin sen varmistaminen, että kohde lisätään add – menetelmällä ja että getAll: n tulos on luettelo, jota laajennetaan kyseisellä asialla.

tämän testin läpiviemiseen liittyvät toteutustiedot ovat sellaisia, joissa tiimisi käyttää arkkitehtuurikokemustaan, mutta on huolehdittava siitä, että siihen lisätään vain olennaisin koodi. Ostoslistan keräysmallin sisuksia miettiessä ei kannata mennä liiallisuuksiin. Se on liukas tiukka naru, joka voisi helposti pudota kaninkoloon – aivan kuten tuo huonosti muotoiltu metafora 🙂

takaisin töihin!

tässä esimerkissä käytetään propertiesObject argumenttia Object.create määrittelemään list getteri, joka toimii muunneltavana joukkona päivittäistavaraluetteloillemme:

/script/model / grocery-list.js

'use strict';var groceryList = { add: function(item) { this.list.push(item); }, getAll: function() { return this.list; }};module.exports = { create: function() { return Object.create(groceryList, { 'list': { value: , writable: false, enumerable: true } }); }};

jos ajetaan tuo, niin huomataan, että ensimmäinen skenaario menee nyt ohi!

$ ./node_modules/.bin/cucumber-js.....P2 scenarios (1 pending, 1 passed)6 steps (1 pending, 5 passed)

toinen skenaario

tarkastellessamme toisen skenaarion viimeistä vaihetta, vireillä oleva toteutus on pääsemässä lisättyyn kohtaan:

Then I can access that item from the grocery list

jotta tämä vaihe menisi läpi, meidän on tarkistettava, että voimme käyttää Kauppaluetteloon liitettyä tuotetta vetoamalla add() johonkin tuotteeseen.

kuten Päivittäistavarakauppalistan pituuteen pääsemisen toteutuksessa, on olemassa useita tapoja, joilla tämä testi voitaisiin saada läpi koodissa. Jälleen, minusta tuntuu, että tämä on, jos ohjelmistokehityksen kokemus ja maku tulee peliin suhteen arkkitehtuuri, mutta en myös mieluummin yrittää tuottaa vähiten koodia mahdollista; ja olen ensimmäinen myöntää, että joskus menen hieman hajamielinen ja luoda enemmän koodia kuin on tarpeen… siksi, yrittää.

tästä huolimatta meidän on myös otettava huomioon kieli – ja ympäristöerittelyt siinä, miten käsittelemme väitteen läpivientiä-ja selaimella historioineen on monia pohdittavaa. Se ei ole vähättelyä, se on vain ennakkoharkintaa odotusten asettamisessa vaatimuksiin.

erityisesti: olettakaamme, että askel voidaan todentaa Array.indexOf() – menetelmällä Päivittäistavaraluettelon objektista ”getAll ()” palautetusta kokoelmasta? Ilman polyfill, sitten rajoitamme itseämme kulkee väitteitä IE 9 ja vanhemmat. Tällaiset näkökohdat ovat vain jäävuoren huippu päätettäessä siitä, mitä sisällyttää koodebase saada testit läpäisevät, ja todella pitäisi jättää jopa tiimikeskustelu siitä, mitä pidetään tarpeellisena saada tuote tuotantoon.

voisin jatkaa ja jatkaa, mutta oletetaan, että haluamme kattaa kaikki pohjat, kun se tulee selaimet (eli 6 ja ylös, puistattaa). Mielestäni, jotta tämä toinen skenaario muuttuisi vihreäksi, lisäämme getItemIndex() – menetelmän, jossa on seuraava allekirjoitus.:

+ getItemIndex(itemValue):int

ensin muokkaamme vaiheen epäonnistumaan:

/ominaisuus/porrasmääritykset / lisäerä.vaihe.js_

this.Then(/^I can access that item from the grocery list$/, function(callback) { assert.notEqual(myList.getItemIndex(listItem), -1, 'Added item should be found at non-negative index.'); callback();});

tämän testin läpäisemiseksi hyväksytään se, että indeksi, jossa lisätty aine sijaitsee kokoelmassa, ei ole negatiivinen. Tässä skenaariossa emme yritä vahvistaa erittelyä siitä, missä uusi kohde lisätään luetteloon (esim.valmiiksi tai liitteenä), vaan yksinkertaisesti siitä, että se on saatavilla.

käynnissä, joka tuottaa poikkeuksen:

$ ./node_modules/.bin/cucumber-js.....F(::) failed steps (::)TypeError: Object #<Object> has no method 'getItemIndex'

muokataan päivittäistavaraluettelo-objekti tukemaan getItemIndex – menetelmää:

/script/model / päivittäistavaralista.js

'use strict';var groceryList = { add: function(item) { this.list.push(item); }, getAll: function() { return this.list; }, getItemIndex: function(value) { var index = this.list.length; while(--index > -1) { if(this.list === value) { return index; } } return -1; }};module.exports = { create: function() { return Object.create(groceryList, { 'list': { value: , writable: false, enumerable: true } }); }};

toteutuksessa getItemIndex lista käydään läpi ja jos kohta löytyy, indeksi palautetaan. Muussa tapauksessa palautetaan arvo -1. Oleellisesti, miten Array.indexOf – menetelmä toimii ECMAScript 5: ssä.

Huom: tiedän, että voi tuntua hölmöltä käyttää objektia.luo ECMAScript 5, mutta ei Array.Hakemisto. Syy-enimmäkseen-on, että olen yleensä aina mukana polyfill esine.luoda eikä Array.Hakemisto. Kai se on tapa.

nyt, jos ajamme tiedot uudestaan Kurkkujs: n alla:

$ ./node_modules/.bin/cucumber-js......2 scenarios (2 passed)6 steps (6 passed)

kyntemme ovat vihreitä! (Tässä kohtaa pyyhitään otsaa ja taputetaan hitaasti).

Conclusion

tässä viestissä esittelin kuinka käytän BDD-työkalua CucumberJS, jotta voin noudattaa Testivetoista kehitystä Javascriptissä. Kävin läpi käyttämällä esimerkkiä yhdestä ominaisuudesta, jossa on kaksi skenaariota ja kääntämällä epäonnistuneet vaiheet vihreiksi cukeiksi. Jos et tunne prosessia, jossa testit epäonnistuvat ensin vain tuottaa koodia, jotta testi läpäisee, toivon, että seurasit mukana; Saatan olla sanavalmis ja prosessi voi näyttää vievän paljon aikaa, mutta kehitys tällaisten käytäntöjen alla alkaa sujua, kun pääsee uraan. Lisäksi uskon, että on valtava palkinto siitä, että koodisi on testivaljaiden alla, kun on kyse refaktoroinnista ja vikojen korjaamisesta – sekä kehittäjien terveydenhuollossa että liiketoiminnassa.

tämä artikkeli kirjoitettiin alun perin osoitteessa http://custardbelly.com/blog/blog-posts/2014/01/08/bdd-in-js-cucumberjs/index.html

Image courtesy of http://commons.wikimedia.org/wiki/File:Og%C3%B3rki…jpg

e6b2cff6a55928c8f62b9d602add3fed?s=100d = mmr=g

Todd Anderson on sovelluskehittäjä, jolla on intohimo arkkitehtuuriin ja kehitystyön työnkulkuihin.

ketterien käytäntöjen ja Testivetoisen kehityksen vahvana puolestapuhujana, jolla on yli vuosikymmenen kokemus, hän on auttanut web -, mobiili-ja työpöytäratkaisujen toimittamisessa lukuisille yritys-ja viihdeteollisuuden yrityksille, kuten Adobelle, THQ: lle, Condé Nast Publicationsille ja Motorolalle.

hän kirjoittaa usein ohjelmistoista ja teknologiasta blogissaan, tukee avoimen lähdekoodin ohjelmistoja ja antaa parhaani takaisin kehitysyhteisölle Githubin kautta ja on ollut arvostettu ilo olla mukana kirjoittamassa useita nimikkeitä O ’ Reilly ja Wiley Wrox: Amazon profile.

seuraa Toddia Twitterissä

käy Toddin sivuilla

Vastaa

Sähköpostiosoitettasi ei julkaista.