TEST WELL 2014 GRZEGORZ GAŁĘZOWSKI MOTOROLA SOLUTIONS 6 MAR 2014 1 Title Page Option 3 Solid-colored Background Date/location information is Arial16pt. Headline copy is Arial Bold 54pt in all caps. Use Paragraph Line Spacing Multiple of 0.8 with 0pt spacing before and after. Presenter information is Arial Bold 18pt in all caps. The Paragraph Line Spacing is Exactly 0.9pt with 0pt spacing before and after. Background can be changed to any color is white. GRZEGORZ GAŁĘZOWSKI MOTOROLA SOLUTIONS 1
NIE ROZWIĄZUJ W TESTACH JEDNOSTKOWYCH PROBLEMÓW Z TESTOWANYM KODEM Title Page Option 3 Solid-colored Background Date/location information is Arial16pt. Headline copy is Arial Bold 54pt in all caps. Use Paragraph Line Spacing Multiple of 0.8 with 0pt spacing before and after. Presenter information is Arial Bold 18pt in all caps. The Paragraph Line Spacing is Exactly 0.9pt with 0pt spacing before and after. Background can be changed to any color is white. GRZEGORZ GAŁĘZOWSKI MOTOROLA SOLUTIONS 2
BĘDZIE O... TESTACH JEDNOSTKOWYCH I PROJEKTOWANIU ORAZ ICH ZWIĄZKU NA 5 PRZYKŁADACH Agenda Slide Example Note: All slide titles in deck are 44pt BOLD ALL CAPS Arial, Subject Titles 24pt Bold ALL CAPS, Arial. Title color should be consistent with color in presentation. To change title colors you will need to do so in Slide Master view. Images can be swapped out using any of the new brand imagery at www.motorolasolutionscollective.com, go to page 21 for instructions on sizing/modifying. Image for agenda can have three sided bleed.
TESTY W MOIM ŚWIECIE... Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 5
Lista TODO Czy zaimplementowałem wszystko?
Analiza Czy wiem, co buduję?
Wykonywalna dokumentacja Czego "klient" może od kodu oczekiwać?
Projektowanie Jak rozbić problem?
TESTY JEDNOSTKOWE DAWNIEJ I DZIŚ Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 10
"the tests must be kept to the same level of high quality as the production code. (...) Duplication must be eliminated from them." Robert C. Martin
ROZWIĄZANIA NA POZIOMIE AUTOMATYZACJI Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 12
Metody SetUp [SetUp] public void SetTheStage() { this.valueUsedEverywhere = 12; }
Dziedziczenie Klas [TestFixture] public class AuthorizationBehaviors : TestBase
Metody pomocnicze message = createMessageFor("Ala");
JAKOŚĆ JEDNOSTKI: SPÓJNOŚĆ I POWIĄZANIA Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 17
Spójność ~ jednA rzecz A B C D
Spójność ~ jedna rzecz B A C D
Spójność ~ jedna rzecz B A C D
Powiązania – kogo klasa zna? B C D
Mocki – "symulatory" obiektów TEST
SPÓJNOŚĆ I POWIĄZANIA a testy Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 23
Test jednostkowy to klient kodu Jednostka Test Test Test Test
Źle zaprojektowana jednostka... Test Klient Test Test Jednostka Test Test Test Test
...jest trudno testowalna Klient Test Test Jednostka Test Test Test Test
SŁUCHAJ ŚLEPO SWOICH TESTÓW Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 27
Korzyści z ˶słuchania testów˝ Lepszy design produktu większa spójność mniej powiązań Lepsza utrzymywalność testów
PIĘĆ PRZYKŁADÓW 29 Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 29
UWAGA! ZŁY KOD!
Przykład 1: nachodzenie zakresu testów Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 31
validation = new SanityValidation(); info = new PersonInfo(); info validation = new SanityValidation(); info = new PersonInfo(); info.Name = "Zenek"; info.Surname = "Kowalski"; info.Age = VALID_AGE; Assert.DoesNotThrow( () => validation.Of(info) );
validation = new SanityValidation(); info = new PersonInfo(); info validation = new SanityValidation(); info = new PersonInfo(); info.Name = "$"; info.Surname = "Kowalski"; info.Age = VALID_AGE; Assert.Throws<InvalidValueException>( () => validation.Of(info) );
validation = new SanityValidation(); info = new PersonInfo(); info validation = new SanityValidation(); info = new PersonInfo(); info.Name = "Zenek"; info.Surname = "$"; info.Age = VALID_AGE; Assert.Throws<InvalidValueException>( () => validation.Of(info) );
validation = new SanityValidation(); info = new PersonInfo(); info validation = new SanityValidation(); info = new PersonInfo(); info.Name = "Zenek"; info.Surname = "Kowalski"; info.Age = VALID_AGE – 1; Assert.Throws<InvalidValueException>( () => validation.Of(info) );
Póki co... Name Surname Age
Utrzymywalność Name Surname Age Country
Nachodzenie na siebie zakresów testów. Utrzymywalność Name Surname Age Country Wskazówka: Nachodzenie na siebie zakresów testów.
˶Nadpisz jedno pole˝ - OK, ale... validation = new SanityValidation(); info = CreateValidPersonInfo(); info.Name = CreateInvalidName(); Assert.Throws<InvalidValueException>( () => validation.Of(info) );
˶Nadpisz jedno pole˝ - OK, ale... validation = new SanityValidation(); info = CreateValidPersonInfo(); info.Name = CreateInvalidName(); Assert.Throws<InvalidValueException>( () => validation.Of(info) ); Wskazówka: Testy różnych zachowań padają z tego samego powodu
SanityValidation mała spójność Testy Name Of(x) Walidacja Name Testy Surname Walidacja Surname Walidacja Surname Testy Age Walidacja Age Walidacja Age
zachowania sobie przeszkadzają SanityValidation Testy Name Of(x) Walidacja Name Testy Surname Walidacja Surname Testy Age Walidacja Age
zachowania sobie przeszkadzają SanityValidation Testy Name Of() Walidacja Name Testy Surname Walidacja Surname Walidacja Surname Testy Age Walidacja Age Walidacja Age
zachowania sobie przeszkadzają SanityValidation Testy Name Of(x) Walidacja Name Testy Surname Walidacja Surname Walidacja Surname Testy Age Walidacja Age Walidacja Age
Podział lepsza spojnosć SanityValidation PartialValidations Of(x) Walidacja Name Walidacja Surname PersonInfo Walidacja Age
Uwspólnienie SanityValidation PartialValidations Of(x) Walidacja Napisu PersonInfo Walidacja Minimum
Mamy dane i funkcję SanityValidation PartialValidations Of(x) Walidacja Napisu PersonInfo Walidacja Minimum
lepsza abstrakcja Person PartialValidations Validate() Walidacja Napisu PersonInfo Walidacja Minimum
Test Person: używa walidacji? Test Validate() Validate() PersonInfo Partial Validations Mock
Testy walidacji napisu PartialValidations Testy Napisu Walidacja Napisu Testy Minimum Walidacja Minimum
Test walidacji minimum Validations Testy Napisu Walidacja Napisu Testy Minimum Walidacja Minimum
Przykład 2: kombinacje funkcjonalności Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 52
var processing = new MessageProcessing(); messageProcessing var processing = new MessageProcessing(); messageProcessing.Encryption = true; messageProcessing.Translation = true; messageProcessing.Compression = true; processing.For(message); ...
Wskazówka: Eksplozja Kombinacji var processing = new MessageProcessing(); messageProcessing.Encryption = true; messageProcessing.Translation = true; messageProcessing.Compression = true; processing.For(message); ... Wskazówka: Eksplozja Kombinacji
[TestCase(true, true, true)] public void ShouldProcessMessage( bool encrypt, bool translate, bool compress) { processing = new MessageProcessing(); processing.Encryption = encrypt; processing.Translation = translate; processing.Compression = compress; //...
[TestCase(true , true , true )] [TestCase(false, true , true )] [TestCase(true , false, true )] [TestCase(true , true , false)] [TestCase(false, false, true )] [TestCase(true , false, false)] [TestCase(false, true , false)] [TestCase(false, false, false)] public void ShouldProcessMessage( bool encrypt, bool translate, bool compress)
Zbyt wiele odpowiedzialności MessageProcessing For(msg) Encryption Translation Walidacja Surname Compression Walidacja Age
Podział obiektów Encryption For(msg) Translation Compression MessageProcessing For(msg) Encryption Translation Compression
Wyłączanie transformacji MessageProcessing For(msg) NullEncryption Translation Compression
Test przetwarzania - lepiej, ale... MessageProcessing (1) For(msg) (2) (3)
Test przetwarzania - lepiej, ale... MessageProcessing For(msg) Wskazówka: Dużo różnych mocków na test
Wspólny mianownik... Encryption For(msg) Translation Compression MessageProcessing For(msg) Encryption Translation Compression
Podział obiektów Transformation Transformation For(m) Transformation MessageProcessing Transformation Transformation For(m) Transformation Translation Encryption Compression
Test Przetwarzania – jeszcze raz MessageProcessing For(m)
Przykład 3: łańcuchy zależności Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 65
Mocki - legenda Utworzenie: mock = Substitute.For<Interface>() Zaprogramowanie rezultatu wywołanie metody: mock.GetSomething().Returns(something);
system = Substitute. For<System>(); radio = Substitute system = Substitute.For<System>(); radio = Substitute.For<Radio>(); owner = Substitute.For<User>(); system.GetRadio(1).Returns(radio); radio.GetOwner().Returns(owner); owner.GetName().Returns(name); struktura odzwierciedla świat rzeczywisty. Nawigowanie po łańcuchu może prowadzić do problemów. Zamiast tego mogę próbować to przykryć fasadą. problem z enkapsulacją. Pokazać poprawny przykład. Zmienić zamiast danych zwraca boola?
Zestawianie łańcucha mocków Test Name
Wszystko widoczne publicznie Klient System Radio Owner Name
Problem z zależnościami Klient System Radio Owner Name
Problem z zależnościami Klient System Radio Wskazówka: mocki zwracające mocki Owner Name
najprostsze Rozwiązanie Klient System Radio Owner RadioOwnerName
Łatwiejsze testy Klient RadioOwnerName
Przykład 4: muszę testować prywatne metody Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 74
- Chcę testować prywatną metodę Obiekt A() B() D() C()
- Zrób to przez publiczne API Test Obiekt A() B() D() C()
- Ale ona jest na tyle odrębna... Test 2 Obiekt A() B() D() C()
... że chcę ją testować osobno. Obiekt A() Wskazówka: Chęć testowania prywatnych metod B() D() C()
- To ją wydziel! Test 2 Obiekt Inny Obiekt A() B() D() C()
Przykład 5: ciężki kontekst Section Slide Example – No Image, Solid Color TITLE ALL CAPS ARIAL 44 PT BOLD, line spacing .8 lines. A divider slide can use a color block on half the page, color consistent with color choice used in presentation. 80
configData = new ConfigData(); configData. GetHeader() configData = new ConfigData(); configData.GetHeader().SetStdState(StdState.SUBMITTED); configData.GetHeader().SetType(FileType.NECB); ge1 = CreateGroupEntry(1, TALKGROUP, null, configData.Id); ge2 = CreateGroupEntry(2, TALKGROUP, null, configData.Id); ge3 = CreateGroupEntry(3, MULTIGROUP, null, configData.Id); ge4 = CreateGroupEntry(4, MULTIGROUP, null, configData.Id); ge5 = CreateGroupEntry(5, MULTIGROUP, null, configData.Id); talkgroup1 = CreateTalkgroup(1, 1, null, configData.Id); talkgroup2 = CreateTalkgroup(1, 3, null, configData.Id); talkgroup3 = CreateTalkgroup(2, 0, null, configData.Id); talkgroup4 = CreateTalkgroup(3, 5, null, configData.Id); context = new ValidationObjectContext(configData);
pracochłonne ustawianie kontekstu configData = new ConfigData(); configData.GetHeader().SetStdState(StdState.SUBMITTED); configData.GetHeader().SetType(FileType.NECB); ge1 = CreateGroupEntry(1, TALKGROUP, null, configData.Id); ge2 = CreateGroupEntry(2, TALKGROUP, null, configData.Id); ge3 = CreateGroupEntry(3, MULTIGROUP, null, configData.Id); ge4 = CreateGroupEntry(4, MULTIGROUP, null, configData.Id); ge5 = CreateGroupEntry(5, MULTIGROUP, null, configData.Id); talkgroup1 = CreateTalkgroup(1, 1, null, configData.Id); talkgroup2 = CreateTalkgroup(1, 3, null, configData.Id); talkgroup3 = CreateTalkgroup(2, 0, null, configData.Id); talkgroup4 = CreateTalkgroup(3, 5, null, configData.Id); context = new ValidationObjectContext(configData); Wskazówka: pracochłonne ustawianie kontekstu
wnioski Słuchaj ślepo swoich testów Uważaj na: eksplozje kombinacji długie testy testowanie prywatnych metod mocki zwracające mocki ciężki kontekst Szukaj najpierw źródła problemu w designie
pytania? Słuchaj ślepo swoich testów Uważaj na: eksplozje kombinacji długie testy testowanie prywatnych metod mocki zwracające mocki ciężki kontekst Szukaj najpierw źródła problemu w designie
85 Thank you or Q&A wrap up slide example. Font arial 80pt. Legal disclaimer bottom of page arial 8pt font with I Protect iProtect Classification: Public MOTOROLA, MOTO, MOTOROLA SOLUTIONS and the Stylized M Logo are trademarks or registered trademarks of Motorola Trademark Holdings, LLC and are used under license. All other trademarks are the property of their respective owners. © 2010 Motorola, Inc. All rights reserved. 85
Tests As Design Rot Radar Synaesthesia S. Freeman, N. Pryce Test Reflexology A. Kolsky, S. Bain Tests As Design Rot Radar J. Grenning
Lepiej, ale... var encryption = Substitute.For<Encryption>(); var translation = Substitute.For<Translation>(); var compression = Substitute.For<Compression>(); var processing = new MessageProcessing( encryption, translation, compression );
Dużo różnych mocków na test Lepiej, ale... var encryption = Substitute.For<Encryption>(); var translation = Substitute.For<Translation>(); var compression = Substitute.For<Compression>(); var processing = new MessageProcessing( encryption, translation, compression ); Wskazówka: Dużo różnych mocków na test
var transformations = new[] { Substitute var transformations = new[] { Substitute.For<Transformation>(); }; var processing = new MessageProcessing( transformations );
Co testy próbują powiedzieć? public bool Of(PersonInfo info) { if(info.Name == null) throw new InvalidValueException(); if(info.Surname == null) if(info.Age < VALID_AGE) }
var v = Substitute. For<Validation>(); var info = Any var v = Substitute.For<Validation>(); var info = Any.Instance<PersonInfo>(); var person = new Person(info); person.ValidateUsing(v); v.Received().OfString(info.Name); v.Received().OfString(info.Surname); v.Received().OfMin(VALID_AGE, info.Age);
var inbound = new InboundMessaging( var inbound = new InboundMessaging(...); var privateObject = new PrivateObject(inbound); var frame = (Frame)privateObject .Invoke("ParseInput", bytes); Assert.AreEqual(12, frame.Field1); Rozwiązanie 1: testuj przez publiczne API.
testowanie prywatnych metod var inbound = new InboundMessaging(...); var privateObject = new PrivateObject(inbound); var frame = (Frame)privateObject .Invoke("ParseInput", bytes); Assert.AreEqual(12, frame.Field1); Wskazówka: testowanie prywatnych metod
var parsing = new InboundParsing(. ); var frame = parsing var parsing = new InboundParsing(...); var frame = parsing.Of(bytes); Assert.AreEqual(12, frame.Field1);
Przykład: Inteligentne Mocki
mock. When(m => m. Process(WithAnyMessage()) ) mock.When(m => m.Process(WithAnyMessage()) ).Do(() => { messageCount++; if(messageCount > 100) throw new CountExceededException(); Thread.Sleep(1000); }); TODO zastąpić pseudokodem
Wskazówka: "inteligentne" mocki mock.When(m => m.Process(WithAnyMessage()) ).Do(() => { messageCount++; if(messageCount > 100) throw new CountExceededException(); Thread.Sleep(1000); }); Wskazówka: "inteligentne" mocki