370 likes | 628 Views
TDD. Börda, befrielse eller rent av en omöjlighet?. TDD – kort repetition. Röd Skriv först ett test som inte går igenom Grön Gör enklast möjliga implementation för att testet ska gå igenom Refactor Ta bort duplicerad kod. TDD – repetition rött. public class Person {
E N D
TDD Börda, befrielse eller rent av en omöjlighet?
TDD – kort repetition • Röd • Skriv först ett test som inte går igenom • Grön • Gör enklast möjliga implementation för att testet ska gå igenom • Refactor • Ta bort duplicerad kod
TDD – repetition rött publicclass Person { publicvoid SetGivenName(string s) {_givenName = s;} publicvoid SetSurname(string s) {_surname = s;} publicstring Fullname(){ returnnull;} string _givenName, _surname; } [TestFixture]publicclass PersonTests { [Test] publicvoid FullnameEmptyInitially() { Person p = new Person(); A.AreEqual(string.Empty, p.Fullname()); } }
TDD – repetition grönt publicstring Fullname() { string res = string.Empty; return res; } [TestFixture] publicclass PersonTests { [Test] publicvoid FullnameEmptyInitially() { Person p = new Person(); A.AreEqual(string.Empty, p.Fullname()); } [Test]publicvoid FullnameGivenOnly(){ Person p = new Person(); p.SetGivenName("Ingo"); A.AreEqual("Ingo", p.Fullname()); } [Test]publicvoid FullnameSurnameOnly(){ Person p = new Person(); p.SetSurname("Lundberg"); A.AreEqual("Lundberg", p.Fullname()); } [Test]publicvoid FullnameBoth(){ Person p = new Person(); p.SetGivenName("Ingo"); p.SetSurname("Lundberg"); A.AreEqual("Ingo Lundberg", p.Fullname()); } } publicstring Fullname(){ string res = string.Empty; res += _givenName != null ? _givenName : string.Empty; if (_givenName != null && _surname != null) res += " "; res += _surname != null ? _surname : string.Empty; return res; }
TDD och automatik • Vad är syftet med röd, grön, refactor? • Röd; se till att du testar något • Grön; tja dit vill vi ju • Refactor; Ta bort duplicering och låt inte koden ruttna • Leder TDD automatiskt till god design? • Man måste kunna sina OO-principer … eller? • Patterns följer OO-principer
Viktiga TDD-insikter för mig • Du ska känna förtroende till din egen kod • White box • Mockning skapade känslan för vad som är en Unit och när det verkligen är ett Unit-test • Jag växlar mellan state-based och interaction-based testning (http://www.martinfowler.com/articles/mocksArentStubs.html) • xUnit kan användas för värdefulla tester som i strikt mening inte är Unit tester
TDD och Test • Statement coverage • Vid strikt TDD 100% • Defect insertion • Vilken rad i koden som helst som ”görs till fel” ska leda till rött • Förtroende för din kod • Om överskattat förtroende bug • Vid bug • Hitta felet • Skriva test som exponerar felet • Rätta felet • Vad är alternativet till ej fullständig testsvit? • Inga alls • En större svit • Tillkommer integrations-, system-, acceptanstest, eller vad du nu kallar dem
Mjukvarudesign i allmänhet och objektorienterad design i synnerhet
Ruttnande design • Rigiditet (stelhet) – svårt att ändra då en ändring tvingar dig till ändringar på fler ställen (beroende moduler) • Bräcklighet – en ändring på ett ställe skapar problem på helt andra ställen • Orubblighet – kan inte återanvända koden ens på andra ställen i systemet, än mindre i andra system • Klibbighet – i designen eller miljön • När det är svårt att upprätthålla designen och man tar till hack • Utvecklingsmiljön så seg att man anpassar ändringarna till dess begränsningar • Onödig komplexitet • Onödig repetition • Ogenomtränglighet
Beroendehantering • Förruttnelsen direkt eller indirekt orsakad av olämpliga beroenden mellan moduler • Skapa beroendebrandväggar över vilka beroenden inte propagerar • OO-design == hur man bygger dessa brandväggar • Principer • Tekniker = designmönster
OO-principer • Open Closed Principle; OCP • Liskov Substitution Principle; LSP • Dependency Inversion Principle; DIP • Fler principer på: • http://www.objectmentor.com/resources/articles/Principles_and_Patterns.PDF • http://www.objectmentor.com/resources/articles/srp • Agile Software Development: Principles, Patterns, and Practices av Robert C. Martin
Open-Closed Principle • En modul ska vara öppen för utökningar men stängd för förändringar. (A module should be open for extensions but closed for modification.) • Huh? • Myntat av Bertand Meyer (1988) • OCP är kärnan i möjligheterna/löftena med objektorientering
Liskov’s Substitution Principle • Subtyper måste vara ersättningsbara för sina bastyper (där kod använder bastypen) • Subtypes must be substitutable for their base types • Barbar Liskov (1988)
LSP a.k.a. Design By Contract • DBC - återigen Bertrand Meyer • En metod har Pre- och Post conditions • Rectangle.SetHeight • Pre • Assert: h > 0 • Post • Assert: _h == h • Assert: _w == old._w • Eiffel stöder detta i språket • Kan också uttrycka invarianser
Dependency-Inversion Principle • Högre nivåns moduler ska inte vara beroende av lågnivåmoduler. Båda ska bero på (förlita sig på) abstraktioner. • Den högre nivåns modul står för den viktigaste policyn/strategin/affärsreglerna • Abstraktioner ska inte vara beroende av detaljer. Detaljer ska bero på (förlita sig på) abstraktioner. • Beroende till stabila konkreta klasser (som t.ex. System.String) är inte brott mot DIP. • OCP målet, DIP den primära mekanismen • Vi pratar om beroendebrandväggar nu!
DIP; anti-strukturerad programmering public void Copy() { int c; while((c = ReadKbd()) != EOF) WritePrt(c); } Nytt krav! public void Copy(DeviceReader r, DeviceWriter w) { int c; while((c = r.Read()) != EOF) w.Write(c); }
TDD och design eller vad har OO-principer och designmönster med TDD att göra?
Testbarhet är #1 • Läsbar kod • För vem är kod läsbar? • Gemensamma referensramar • Testbarhet går före läsbarhet • De automatiska testerna i sig berättar om koden
Typiska delar i en app • Domain Model • Persistent via O/R-mappning (object/relational) • Persistence • Data Access Objects • GUI modell • GUI
Typiska delar i en app • TDD av Model • TDD av WebGuiMdl • Test med xUnit av Persistence • Ev. test av WebGuiMdlGlue
TDD av DM • Testa objekt utan att bekymra sig om hur de lagras • POCO; Plain Old CLR Objects • POJO; Plain Old Java Objects • Publika metoder • State-baserad testning (se exempel snart) • Kan involvera mockning/stubbning men inte givet att det gör det
Ett fall för DM • Någon kommer fram till disken med en bok i handen och vill låna den • Bibliotekarie • Låntagare • Bokexemplar
Ett fall för DM [SetUp]protectedvoid Setup() { librarian = new Librarian(); book = new Book("Foo"); bookCopy = new BookCopy(book, 123); borrower = new Borrower(1); } [Test]publicvoid CheckOut() { librarian.CheckOut(borrower, bookCopy); A.AreEqual(borrower, bookCopy.BorrowedTo); A.IsTrue(borrower.IsBorrowing(bookCopy)); }
TDD av GUI • Steg nummer 1; bort med kod från formuläret • Är detta månne ett större problem i MS-världen? • Så lite kod som möjligt ska vara otestad • Testar inte utseende med xUnit • Interaction-based testning (mockning) • State-based asserts förekommer
GUI; Mockning av samarbetspartners [SetUp] protectedvoid Setup() { foo = new Book("Foo"); bar = new Book("Bar"); IList books = new ArrayList(); books.Add(foo); books.Add(bar); coll = new DynamicMock(typeof(BookListingMdl.Collab)); coll.SetupResult("GetBooks", books); mdl = new BookListingMdl( (BookListingMdl.Collab)coll.MockInstance); view = new DynamicMock(typeof(BookListingView)); mdl.SetView((BookListingView)view.MockInstance); } [Test]publicvoid Render(){ view.Expect("BookEntry", "Bar", !SEL); view.Expect("BookEntry", "Foo", !SEL); mdl.Render(); view.Verify(); }
GUI; Den testade koden publicvoid Render() { for(int i=0; i<_books.Count; i++) { Book book = (Book)_books[i]; bool selected = i == _selInx; _view.BookEntry(book.Title, selected); } }
DIP • Kravet på test-barhet leder till en lösning enligt Dependency Inversion Principle • Jag menar det
(Web) Service • Byt WebGuiMdl mot SvcMdl • Byt WebGuiMdlGlue mot SvcMdlGlue
Andra tester • Persistence • Preparera data i databasen • Kör någon/några DAOs • Rensa i databasen • ”Klister” • Integrationstest, kan använda xUnit
Rubriken Nå? Hur var det nu? Börda, befrielse, …
Börda • Kostnad i att lära sig nytt arbetssätt • Skriva tester • Men det är ju kul! • Du behöver inte vänta till första underhållssvängen för att testerna ska ge återbäring • Måste behärska OO? • Kanske, kanske inte … men ganska många av mina bilder handlar om OO generellt utan direkt koppling till TDD • Kanske andra ser det annorlunda? • Jag råkar ha ganska mycket OO med mig in i TDD-land
Befrielse • Ingenjörsdisciplin • Metodik på en för utvecklaren relevant nivå • Stödjer dig i kampen mot förruttnelsen (OO == beroendehantering) • Kvalitet • Våga ändra/förbättra kod • Utan tester vet man inte om saker rasar samman på andra ställen • Med åren blev iaf jag försiktigare med dans på slak lina • Med tester kan vi sluta spekulera vad som ska ”OCP:as” • Bra för oss som utvecklare • Bra för kunden • Tillfredsställelse • Rött • Grönt
(O)möjlighet • Naturligtvis inte! • Det mesta går att testa • Bryt ut klasser i eget paket • Alla xUnit tester är dock inte TDD • Men vem bryr sig då man har en bra uppsättning automatiska, snabbt exekverande, tester?
Referenser • www.objectmentor.com • www.xprogramming.com • www.nunit.org • www.nmock.org
Ingo • www.ingolundberg.com • www.knowit.se • www.dev112.com