Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Tdd objective c

1,898 views

Published on

  • Be the first to comment

  • Be the first to like this

Tdd objective c

  1. 1. Разработка через тестирование iOS, Bowling Game Kata Test Driven Development Ivan Dyachenko <ivan.dyachenko@gmail.com>© Luxoft Training 2012 http://uamobile.in.ua/ Thursday, December 6, 12
  2. 2. Kent Beck Экстремальное программирование (XP) Разработка через тестирование (TDD) Agile Manifesto в 2001 году.© Luxoft Training 2012 Thursday, December 6, 12
  3. 3. Whole Team Collective Coding Test-Driven Ownership Standard Development Customer Pair Planning Refactoring Tests Programming Game Continuous Sustainable Simple Design Integration Pace Metaphor Small Releases© Luxoft Training 2012 Extreme Programming Practices Thursday, December 6, 12
  4. 4. Whole Team Collective Coding Test-Driven Ownership Standard Development Customer Pair Planning Refactoring Tests Programming Game Continuous Sustainable Simple Design Integration Pace Metaphor Small Releases© Luxoft Training 2012 Extreme Programming Practices Thursday, December 6, 12
  5. 5. TDD «Чистый код, который работает»© Luxoft Training 2012 Thursday, December 6, 12
  6. 6. Мифы TDD Unit tests == TDD TDD == 100% coverage TDD == время * 2 TDD == серебряная пуля© Luxoft Training 2012 Thursday, December 6, 12
  7. 7. Два простых правила TDD 1 Пишем новый код только тогда, когда автоматический код не сработал 2 Удаляем дублирование© Luxoft Training 2012 Thursday, December 6, 12
  8. 8. © Luxoft Training 2012 Мантра TDD – “red, green, refactor” Thursday, December 6, 12
  9. 9. RED / GREEN / REFACTOR 0 Подумай© Luxoft Training 2012 Thursday, December 6, 12
  10. 10. RED / GREEN / REFACTOR 0 Подумай 1 Напиши тест© Luxoft Training 2012 Thursday, December 6, 12
  11. 11. RED / GREEN / REFACTOR 0 Подумай 1 Напиши тест 2 Скомпилируй© Luxoft Training 2012 Thursday, December 6, 12
  12. 12. RED / GREEN / REFACTOR 0 Подумай 1 Напиши тест 2 Скомпилируй 3 Исправь ошибки© Luxoft Training 2012 Thursday, December 6, 12
  13. 13. RED / GREEN / REFACTOR 0 Подумай 1 Напиши тест 2 Скомпилируй 3 Исправь ошибки 4 Запусти и убедись что тесты упали© Luxoft Training 2012 Thursday, December 6, 12
  14. 14. RED / GREEN / REFACTOR 0 Подумай 1 Напиши тест 2 Скомпилируй 3 Исправь ошибки 4 Запусти и убедись что тесты упали 5 Напиши код© Luxoft Training 2012 Thursday, December 6, 12
  15. 15. RED / GREEN / REFACTOR 0 Подумай 1 Напиши тест 2 Скомпилируй 3 Исправь ошибки 4 Запусти и убедись что тесты упали 5 Напиши код 6 Запусти и убедись что тесты прошли© Luxoft Training 2012 Thursday, December 6, 12
  16. 16. RED / GREEN / REFACTOR 0 Подумай 1 Напиши тест 2 Скомпилируй 3 Исправь ошибки 4 Запусти и убедись что тесты упали 5 Напиши код 6 Запусти и убедись что тесты прошли© Luxoft Training 2012 7 Рефакторинг Thursday, December 6, 12
  17. 17. RED / GREEN / REFACTOR 0 Подумай 1 Напиши тест 2 Скомпилируй 3 Исправь ошибки Повтори 4 Запусти и убедись что тесты упали 5 Напиши код 6 Запусти и убедись что тесты прошли© Luxoft Training 2012 7 Рефакторинг Thursday, December 6, 12
  18. 18. Ошибки TDD Писать тест после кода Писать тест который сразу проходит Писать сразу много тестов© Luxoft Training 2012 Thursday, December 6, 12
  19. 19. Инструменты § OCUnit/SenTestingKit начиная IPhone SDK 2.2 § GHUnit § Google Toolkit for Mac § OCMock § OCHamcrest § Kiwi § Cedar § …© Luxoft Training 2012 Thursday, December 6, 12
  20. 20. OCUnit § Object-oriented tests § Very low overhead § Tests are in the same language as the implementation § Test case classes cohabit with classes they test § Promotes aggressive refactoring § Helps capture requirements, expose dependencies § Total integration in the development process § Records testing expertise© Luxoft Training 2012 § Tests frameworks, bundles, or applications § Easy installation Thursday, December 6, 12
  21. 21. OCUnit Asserts TestCase § STAssertNil § test<Test Case> § STAssertNotNil § setUp § STAssertTrue § tearDown § STAssertFalse § STAssertEquals § STAssertEqualObjects § STFail© Luxoft Training 2012 § ... Thursday, December 6, 12
  22. 22. Пример Bowling Game Kata - подсчет очков игры в боулинг www.objectmentor.com wiki.agiledev.ru© Luxoft Training 2012 www.slideshare.net Thursday, December 6, 12
  23. 23. © Luxoft Training 2012 Thursday, December 6, 12
  24. 24. Правила игры в боулинг Раунды Подсчет очков § 10 раундов (frame-ов) § 10 сразу (strike). Заканчивается досрочно. § В одном  frame – 2 броска 10 очков + pins + pins § Цель – выбить кегли (pins) § 10 очков со второго броска (spare) § Выигрывает набравший больше 10 очков + pins всех очков § Max 300 очков (12 strikes) Последний frame § 1-й strike – дает возможность бросить еще два раза© Luxoft Training 2012 § 2-й spare – дает возможность бросить еще одни раз Thursday, December 6, 12
  25. 25. Надо написать BowlingGame + roll(pins : int) + score() : int Написать класс BowlingGame, который имеет два метода • roll (pins : int) – вызывается каждый раз когда игрок бросает шар. Pins – количество выбитых кеглей в этом броске • score() : int – вызывается в конце игры. Показывает результат игры© Luxoft Training 2012 Thursday, December 6, 12
  26. 26. A quick design session Нам нужен BowlingGame класс BowlingGame + roll(pins : int)© Luxoft Training 2012 Thursday, December 6, 12
  27. 27. A quick design session BowlingGame Frame + roll(pins : int) + score() : int Игра имеет 10 фреймов© Luxoft Training 2012 Thursday, December 6, 12
  28. 28. A quick design session BowlingGame Frame Roll + roll(pins : int) + score() : int - pins : int Каждый фрейм состоит из одного или двух бросков© Luxoft Training 2012 Thursday, December 6, 12
  29. 29. A quick design session BowlingGame Frame Roll + roll(pins : int) + score() : int - pins : int Tenth Frame Последний фрейм© Luxoft Training 2012 содержит два или три броска Thursday, December 6, 12
  30. 30. A quick design session BowlingGame Frame Roll + roll(pins : int) + score() : int - pins : int Score функция должна пройтись по всем фреймам и посчитать результат Tenth Frame© Luxoft Training 2012 Thursday, December 6, 12
  31. 31. A quick design session Strike или Spare зависит от количества Next Frame выбитых кеглей в Frame BowlingGame Frame Roll + roll(pins : int) + score() : int - pins : int Tenth Frame© Luxoft Training 2012 Thursday, December 6, 12
  32. 32. Готовим среду для тестирования +© Luxoft Training 2012 Thursday, December 6, 12
  33. 33. Еще раз повторим правила Раунды Подсчет очков § 10 раундов (frame-ов) § 10 сразу (strike). Заканчивается досрочно. § В одном  frame – 2 броска 10 очков + pins + pins § Цель – выбить кегли (pins) § 10 очков со второго броска (spare) § Выигрывает набравший больше 10 очков + pins всех очков § Max 300 очков (12 strikes) Последний frame § 1-й strike – дает возможность бросить еще два раза© Luxoft Training 2012 § 2-й spare – дает возможность бросить еще одни раз Thursday, December 6, 12
  34. 34. Пишем код© Luxoft Training 2012 Thursday, December 6, 12
  35. 35. Test List /* should score zero on gutter game Подумай should score twenty when all one should score spare correctly should score all spares correctly should score strike correctly should score super game */ #import "BowlingGameTests.h” @implementation BowlingGameTests - (void)setUp { [super setUp]; } - (void)tearDown { [super tearDown];© Luxoft Training 2012 } @end Thursday, December 6, 12
  36. 36. Assert First - (void)testGutterGame { STAssertEquals(0, [game score], nil); }© Luxoft Training 2012 Thursday, December 6, 12
  37. 37. Test First - (void)testGutterGame { BowlingGame *game = [[BowlingGame alloc] init]; for (int i = 0; i < 20; i++) { [game roll:0]; } STAssertEquals(0, [game score], nil); } Пишем тест игнорируя ошибки компилятора© Luxoft Training 2012 Thursday, December 6, 12
  38. 38. Исправляем ошибки, пишем код - (void)testGutterGame { BowlingGame *game = [[BowlingGame alloc] init]; for (int i = 0; i < 20; i++) { [game roll:0]; } STAssertEquals(0, [game score], nil); } @implementation BowlingGame - (void)roll:(int)pins { } - (int)score { Пишем минимум return 0; кода! KISS }© Luxoft Training 2012 @end Thursday, December 6, 12
  39. 39. Следующий тест - (void)testAllOne Тест падает { BowlingGame *game = [[BowlingGame alloc] init]; for (int i = 0; i < 20; i++) { [game roll:1]; } STAssertEquals(20, [game score], nil); }© Luxoft Training 2012 Thursday, December 6, 12
  40. 40. Пишем код - (void)testAllOne { BowlingGame *game = [[BowlingGame alloc] init]; for (int i = 0; i < 20; i++) { [game roll:1]; Тест проходит } STAssertEquals(20, [game score], nil); } #import "BowlingGame.h" @implementation BowlingGame - (void)roll:(int)pins { score += pins; } - (int)score { return score; }© Luxoft Training 2012 @end Thursday, December 6, 12
  41. 41. Убираем дублирование - (void)testGutterGame { int n = 20; int pins = 0; for (int i = 0; i < n; i++) { [game roll:pins]; } STAssertEquals(0, [game score], nil); }© Luxoft Training 2012 Thursday, December 6, 12
  42. 42. Рефакторим - (void)testGutterGame { [self rollMany:0 times:20]; STAssertEquals(0, [game score], nil); } - (void)testAllOne { [self rollMany:1 times:20]; STAssertEquals(20, [game score], nil); } - (void)rollMany:(int)pins times:(int)n { for (int i = 0; i < n; i++) { [game roll:pins]; } }© Luxoft Training 2012 Thursday, December 6, 12
  43. 43. Проверка на Spare 6 3 Тест падает 4 - 10 + 3 3 - (void)testScoreSpare { [game roll:5]; // spare [game roll:5]; [game roll:3]; [self rollMany:17 times:0]; STAssertEquals(16, [game score], nil); }© Luxoft Training 2012 Thursday, December 6, 12
  44. 44. Design review #import "BowlingGame.h" roll – занимается подсчетом очков, но @implementation BowlingGame имя метода не указывает на это - (void)roll:(int)pins { score += pins; } - (int)score { return score; } score – не занимается подсчетом @end очков, хотя имя указывает, что должна© Luxoft Training 2012 Thursday, December 6, 12
  45. 45. Redesign Добавляем метод в Ignore //- (void)testScoreSpare { // [game roll:5]; // spare // [game roll:5]; // [game roll:3]; // [self rollMany:17 times:0]; // STAssertEquals(16, [game score], nil); //}© Luxoft Training 2012 Thursday, December 6, 12
  46. 46. Redesign #import "BowlingGame.h" @implementation BowlingGame - (void)roll:(int)pins { rolls[currentRoll++] = pins; } - (int)score { int score = 0; for (int i = 0; i < 20; i++) score += rolls[i]; return score; } @end© Luxoft Training 2012 Thursday, December 6, 12
  47. 47. Убираем Ignore - (void)testScoreSpare { [game roll:5]; // spare [game roll:5]; [game roll:3]; Тест падает [self rollMany:17 times:0]; STAssertEquals(16, [game score], nil); }© Luxoft Training 2012 Thursday, December 6, 12
  48. 48. Пишем код - (int)score { int score = 0; for (int i = 0; i < 20; i++) if (rolls[i] + rolls[i + 1] == 10) //spare score += 10 + rolls[i + 2]; ... score += rolls[i]; return score; } Хорошая идея! За одним исключением – это не работает У нас все еще проблемы с дизайном. Очевидно, надо считать очки по фреймам© Luxoft Training 2012 Thursday, December 6, 12
  49. 49. Очередная сессия Redesign //- (void)testScoreSpare { // [game roll:5]; // spare // [game roll:5]; // [game roll:3]; // [self rollMany:17 times:0]; // Добавляем метод STAssertEquals(16, [game score], nil); //} в Ignore© Luxoft Training 2012 Thursday, December 6, 12
  50. 50. Refactoring - (int)score { int score = 0; int i = 0; for (int frame = 0; frame < 10; frame++) { score += rolls[i] + rolls[i + 1]; i += 2; } return score; }© Luxoft Training 2012 Thursday, December 6, 12
  51. 51. Убираем Ignore - (void)testScoreSpare { [game roll:5]; // spare [game roll:5]; Тест падает [game roll:3]; [self rollMany:17 times:0]; STAssertEquals(16, [game score], nil); }© Luxoft Training 2012 Thursday, December 6, 12
  52. 52. Пишем код - (int)score { int score = 0; int i = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[i] + rolls[i + 1] == 10) {// spare score += 10 + rolls[i + 2]; i += 2; } else { score += rolls[i] + rolls[i + 1]; i += 2; } } return score; }© Luxoft Training 2012 Thursday, December 6, 12
  53. 53. Design review Плохое имя переменной - (int)score { int score = 0; int i = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[i] + rolls[I + 1] == 10) {// spare score += 10 + rolls[i + 2]; i += 2; } Плохо иметь комментарий в else { условии score += rolls[i] + rolls[i + 1]; i += 2; } } return score; }© Luxoft Training 2012 Thursday, December 6, 12
  54. 54. Refactoring - (int)score { int score = 0; int ballIndex = 0; for (int frame = 0; frame < 10; frame++) { if ([self isSpare:ballIndex]) { score += 10 + rolls[ballIndex + 2]; ballIndex += 2; } else { score += rolls[ballIndex] + rolls[ballIndex + 1]; ballIndex += 2; } } return score; } - (BOOL)isSpare:(int)ballIndex {© Luxoft Training 2012 return rolls[ballIndex] + rolls[ballIndex+1] == 10; } Thursday, December 6, 12
  55. 55. Проверка на Strike 10 3 * 4 10 + 3 + 4 3+4 Комментарий в тесте - (void)testScoreStrike { [game roll:10]; // strike [game roll:3]; [game roll:4]; [self rollMany:16 times:0]; STAssertEquals(24, [game score], nil); }© Luxoft Training 2012 Thursday, December 6, 12
  56. 56. Пишем код - (int)score { int score = 0; int ballIndex = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[ballIndex] == 10) { score += 10 + ! ! ! ! ! ! rolls[ballIndex + 1] + ! ! ! ! ! ! ! ! ! rolls[ballIndex + 2]; ballIndex++; } else if ([self isSpare:ballIndex]) { score += 10 + rolls[ballIndex + 2]; ballIndex += 2; } else { score += rolls[ballIndex] + rolls[ballIndex + 1]; ballIndex += 2; } }© Luxoft Training 2012 return score; } Thursday, December 6, 12
  57. 57. Design review - (int)score { int score = 0; Комментарий для условия int ballIndex = 0; for (int frame = 0; frame < 10; frame++) { if (rolls[ballIndex] == 10) { // strike score += 10 + rolls[ballIndex + 1] + rolls[ballIndex + 2]; ballIndex++; } else if ([self isSpare:ballIndex]) { score += 10 + rolls[ballIndex + 2]; ballIndex += 2; } else { score += rolls[ballIndex] + rolls[ballIndex + 1]; ballIndex += 2; } } return score;© Luxoft Training 2012 } Непонятное выражение Thursday, December 6, 12
  58. 58. Refactoring - (int)score { int score = 0; int ballIndex = 0; for (int frame = 0; frame < 10; frame++) { if ([self isStrike:ballIndex]) { score += 10 + [self strikeBonus:ballIndex]; ballIndex++; } else if ([self isSpare:ballIndex]) { score += 10 + [self spareBonus:ballIndex]; ballIndex += 2; } else { score += [self sumOfBallsInFrame:ballIndex]; ballIndex += 2; } } return score; }© Luxoft Training 2012 Thursday, December 6, 12
  59. 59. Refactoring - (void)testScoreStrike { [self rollStrike]; [game roll:3]; [game roll:4]; [self rollMany:16 times:0]; STAssertEquals(24, [game score], nil); } - (void)rollStrike { [game roll:10]; }© Luxoft Training 2012 git checkout bowling-0.8 Thursday, December 6, 12
  60. 60. Контрольный пример - (void)testPrefectGame { [self rollMany:10 times:12]; STAssertEquals(300, [game score], nil); }© Luxoft Training 2012 Thursday, December 6, 12
  61. 61. Cтратегии запуска тестов § IDE § Console § CocoaPods § Maven § Test Runner § CI § File watcher© Luxoft Training 2012 Thursday, December 6, 12
  62. 62. Continuous integration Hudson Team City© Luxoft Training 2012 Thursday, December 6, 12
  63. 63. Преимущества от TDD Меньше ошибок Проще рефакторить Документация Лучший дизайн кода Быстрее процесс разработки© Luxoft Training 2012 Thursday, December 6, 12
  64. 64. BDD Behavior Driven Development (BDD)© Luxoft Training 2012 Thursday, December 6, 12
  65. 65. BDD – что это? Language© Luxoft Training 2012 Thursday, December 6, 12
  66. 66. Difference between TDD, BDD & ATDD TDD ATDD© Luxoft Training 2012 Thursday, December 6, 12
  67. 67. BDD DDD-Domain Driven Testing + DSL + GIVEN / WHEN / THEN Спецификация Тесты Документация© Luxoft Training 2012 Thursday, December 6, 12
  68. 68. Kiwi describe(@"BowlingGame", ^{ it(@"should score zero on gutter game", ^{ rollMany(20, 0); [[theValue([game score]) should] equal:theValue(0)]; }); it(@"should score twenty when all one", ^{ rollMany(20, 1); [[theValue([game score]) should] equal:theValue(20)]; }); it(@"should score spare correctly", ^{ [game roll:5]; [game roll:5]; [game roll:3]; rollMany(17, 0); [[theValue([game score]) should] equal:theValue(16)]; }); it(@"should score strike correctly", ^{ [game roll:10]; [game roll:3];© Luxoft Training 2012 [game roll:4]; rollMany(16, 0); [[theValue([game score]) should] equal:theValue(24)]; }); Thursday, December 6, 12
  69. 69. © Luxoft Training 2012 Thursday, December 6, 12
  70. 70. © Luxoft Training 2012 Thursday, December 6, 12
  71. 71. *.feature # language: ru Функционал: Авторизация пользователей Чтобы указывать себя автором снипетов, голосовать за снипеты и нарабатывать карму, пользователи должны иметь возможность регистрироваться Сценарий: Успешная авторизация с указываемыми логином и паролем Допустим я зарегистрированный пользователь "User12" с паролем "User1212" И я на странице Авторизация Если ввожу в поле Логин "User12" И ввожу в поле Пароль "User1212" И кликаю кнопку "Войти" То я должен увидеть уведомление "Добро пожаловать!" И я должен оказаться на странице Страница пользователя© Luxoft Training 2012 Thursday, December 6, 12
  72. 72. *.feature© Luxoft Training 2012 Thursday, December 6, 12
  73. 73. Вопросы ?© Luxoft Training 2012 Thursday, December 6, 12
  74. 74. Разработка через тестирование IDyachenko@luxoft.com git clone git://github.com/ivan-dyachenko/Trainings.git© Luxoft Training 2012 https://github.com/ivan-dyachenko/Trainings Thursday, December 6, 12

×