Sveiki,
vakar buvo labai įdomi susitikimo tema ir turinys, ačiū organizatoriams! Norėjau tik pridurt, kad šiuo metu vis labiau vietoj TDD ryškėja BDD (Behavior Driven Development). Pats perėjau prie jo gal jau prieš pusės metų, ir tikrai nesigailiu. Šiaip esmė labai panaši kaip ir TDD tik forma biški skiriasi. Privalumai tokie IMO
pvz kodas (naudoju MSpec, kaip BDD frameworką):
public class when_foo_is_called { static Foo foo; static string result; Establish context = () => foo = new Foo(); Because of = () => result = foo.DoSomething(); It should_not_return_null = () => result.ShouldNotBeNull(); It should_return_hello = () => result.ShouldEqual("Hello"); It should_return_something_else; // šitas neimplementuotas }
Sintaksė truputi neįprasta, bet pati ta forma, man bent jau, yra gana aiški.
trūkumai labiausiai tokie, kad frameworkas yra naujas, ir mažai dar įrankių "draugauja" su juom. Dar man nepatiko, kad tas It pjaunasi su Moq It, bet čia jau yra kaip yra. O taip, tai visai viskas lyg ir gerai, esu patenkintas.
Gal yra, kas irgi bandė tą BDD, gal susidūrėt su kokiom problemom?
Čia toks straipsnelis pamastymams, su kuriuo esu linkęs sutikti, nors dar ir nenaudojau BDD:
http://blog.orfjackal.net/2010/05/choice-of-words-in-testing-frameworks.html
įdomus straipsnis, čia kaip matau autorius nepatenkintas It raktažodžiu, nes jis specifikacijos pavadinimą įdeda į rėmus. Šiaip aš nematau čia didelės problemos. Čia klausimas tik kaip tu pavadinsi tą pagalbinį delegatą, galima The, arba Spec, kuris išvis nedarytų įtakos testo funkcijos sakiniui. Be to tas It, bent jau MSpec, neįeina į dokumentaciją ir atvaizdavimą testo runninimo metu. Pvz mano pavyzdys būtų atvaizduotas taip:
tai jam šiaip niekas kaip ir netrukdo parašyti testą, kaip jis nori. Tai yra taip:
Fibonacci numbers: - The first two Fibonacci numbers are 0 and 1 - Each remaining number is the sum of the previous two
Man asmeniškai kol kas labiau patinka TDD testai, naudojant Roy Osherove knygoje 'The Art of Unit Testing' aprašytą naming convention, t.y.:
MetodoKuriKvieciamPavadinimas_Situacija_RezultataiKuriuTikimasi
Pavyzdžiui, "Pop_WhenStackIsEmpty_ThrowsStackEmptyException", "Foo_GivenEmptyString_ReturnsNull" ir t.t. Nesakau, kad BDD yra blogai, rekia pačiam pasibandyti įvairius frameworkus, bet kol kas man natūraliau skaitosi taip :)
O tavo pavyzdyje, iš pirmo žvilgsnio matau tokius 'minusus':
Taigi, nelabai matau, kodėl tai būtų mažiau kodo rašymo, nei AAA principu. Pataisyk, jei klystu :)
taisau :)
Gediminas: Atskirų klaisių kūrimas kiekvieno metodo testavimui
Atskirų klaisių kūrimas kiekvieno metodo testavimui
ne, čia anonymous delegate'as galima rašyti ir ilgesnį variantą.
kažką tokio
Because of = () => {
foo.doSomething(); result = foo.doSomethingElse();
};
Gediminas: Jei reikalingi keli metodo testai skirtingomis sąlygomis, teks kurti skirtingas klases?
Jei reikalingi keli metodo testai skirtingomis sąlygomis, teks kurti skirtingas klases?
Taip skirtingas klases. Skamba grėsmingai, bet pažiūrėjus į tą klasę kažko tokio sunkaus, palyginus su funkcija klasėje, nematau. Aš pavyzdžiui norint ištestuosi skirtingas situacijas, su tuo pačiu kontekstu, tam kontekstui sukuriu bazinę klasę, ir išvestinėse klasėse naudoju tą vieną kontekstą. public abstract class SomeBase { protected static Foo foo; Establish context = () => foo = new Foo(); } public class when_doing_something : SomeBase { static string res; Because of = () => res = foo.DoSomenthing(); It should_return_hello = () => res.ShouldEqual("Hello"); ... } Aš labiau testus grupuoju į namespace'us. MSpec siūlo daugiau variantų, kaip tas klases grupuot ir panašiai.
Gediminas: Taigi, nelabai matau, kodėl tai būtų mažiau kodo rašymo, nei AAA principu. Pataisyk, jei klystu :)
Parodau pavyzdį, kaip aš anksčiau rašiau su unit testais. Pagal unit testų taisyklę, turi išestuoti tik vieną funkcionalumą ir ne daugiau. Todėl man gaudavosi kažkas tokio:
public class SomeFooTest { public void when_do_somenthing_should_be_one_state() { //arrange var foo = Mock<Foo>().Setup(.... // ilgas setupas
//action foo.Do(); //assert Assert.That(foo.State1 == 0); } // copy/paste :) public void when_do_somenthing_should_be_other_state() { //arrange var foo = Mock<Foo>().Setup(.... // ilgas setupas
//action foo.Do(); //assert Assert.That(foo.State2 == 0);
} }
čia labai paprastas pavyzdys, realybė yra sudėtingesnė, aišku, bet esmė tokia. Man BDD šiuo požiūriu yra paprastesnis.
Tai jei turi ilgą pasikartojantį setupą, naudoji Extract Method refactoringą. Jei tas metodas būdingas visiems testams, dedi atributą TestInitialize ar kt., kad jis būtų paleistas prieš kiekvieną testą.
Jei negali padaryt Extract Method, tada, matyt, nesigaus ir Extract Class. Aš taip įsivaizduoju.
Aišku, jei būtų kažkoks konkretus pavyzdys, būtų gal aiškiau, ką turėjai omeny. Bet nemanau, kad arrange skirtumas yra toks didelis, kad šiuo atžvilgiu galima būtų dėti pliusą BDD frameworkam. Kiek mačiau, arrange iš principo atliekamas vienodai.
Na šitas dalykas nėra esminis, neverta gal čia gilintis. Niekur nemačiau, kad jį iškeltų kaip BDD privalumą. Čia aš pagal savo patirtį vertinu. Gal čia lengviau pabandyti pačiupinėti tą BDD ir tada pasijus tie skirtumai labiau.