More Related Content Similar to Tdd objective c
Similar to Tdd objective c (10) Tdd objective c1. Разработка через
тестирование
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. Kent Beck
Экстремальное
программирование (XP)
Разработка через
тестирование (TDD)
Agile Manifesto в 2001
году.
© Luxoft Training 2012
Thursday, December 6, 12
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. 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. TDD
«Чистый код, который работает»
© Luxoft Training 2012
Thursday, December 6, 12
6. Мифы TDD
Unit tests == TDD
TDD == 100% coverage
TDD == время * 2
TDD == серебряная пуля
© Luxoft Training 2012
Thursday, December 6, 12
7. Два простых правила TDD
1 Пишем новый код только тогда, когда
автоматический код не сработал
2 Удаляем дублирование
© Luxoft Training 2012
Thursday, December 6, 12
8. © Luxoft Training 2012
Мантра TDD – “red, green, refactor”
Thursday, December 6, 12
9. RED / GREEN / REFACTOR
0 Подумай
© Luxoft Training 2012
Thursday, December 6, 12
10. RED / GREEN / REFACTOR
0 Подумай
1 Напиши тест
© Luxoft Training 2012
Thursday, December 6, 12
11. RED / GREEN / REFACTOR
0 Подумай
1 Напиши тест
2 Скомпилируй
© Luxoft Training 2012
Thursday, December 6, 12
12. RED / GREEN / REFACTOR
0 Подумай
1 Напиши тест
2 Скомпилируй
3 Исправь ошибки
© Luxoft Training 2012
Thursday, December 6, 12
13. RED / GREEN / REFACTOR
0 Подумай
1 Напиши тест
2 Скомпилируй
3 Исправь ошибки
4 Запусти и убедись что тесты упали
© Luxoft Training 2012
Thursday, December 6, 12
14. RED / GREEN / REFACTOR
0 Подумай
1 Напиши тест
2 Скомпилируй
3 Исправь ошибки
4 Запусти и убедись что тесты упали
5 Напиши код
© Luxoft Training 2012
Thursday, December 6, 12
15. RED / GREEN / REFACTOR
0 Подумай
1 Напиши тест
2 Скомпилируй
3 Исправь ошибки
4 Запусти и убедись что тесты упали
5 Напиши код
6 Запусти и убедись что тесты прошли
© Luxoft Training 2012
Thursday, December 6, 12
16. RED / GREEN / REFACTOR
0 Подумай
1 Напиши тест
2 Скомпилируй
3 Исправь ошибки
4 Запусти и убедись что тесты упали
5 Напиши код
6 Запусти и убедись что тесты прошли
© Luxoft Training 2012
7 Рефакторинг
Thursday, December 6, 12
17. RED / GREEN / REFACTOR
0 Подумай
1 Напиши тест
2 Скомпилируй
3 Исправь ошибки
Повтори
4 Запусти и убедись что тесты упали
5 Напиши код
6 Запусти и убедись что тесты прошли
© Luxoft Training 2012
7 Рефакторинг
Thursday, December 6, 12
18. Ошибки TDD
Писать тест после кода
Писать тест который сразу проходит
Писать сразу много тестов
© Luxoft Training 2012
Thursday, December 6, 12
19. Инструменты
§ OCUnit/SenTestingKit начиная IPhone SDK 2.2
§ GHUnit
§ Google Toolkit for Mac
§ OCMock
§ OCHamcrest
§ Kiwi
§ Cedar
§ …
© Luxoft Training 2012
Thursday, December 6, 12
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. OCUnit
Asserts TestCase
§ STAssertNil § test<Test Case>
§ STAssertNotNil § setUp
§ STAssertTrue § tearDown
§ STAssertFalse
§ STAssertEquals
§ STAssertEqualObjects
§ STFail
© Luxoft Training 2012
§ ...
Thursday, December 6, 12
22. Пример
Bowling Game Kata - подсчет очков игры в боулинг
www.objectmentor.com
wiki.agiledev.ru
© Luxoft Training 2012
www.slideshare.net
Thursday, December 6, 12
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. Надо написать
BowlingGame
+ roll(pins : int)
+ score() : int
Написать класс BowlingGame, который имеет два метода
• roll (pins : int) – вызывается каждый раз когда игрок
бросает шар. Pins – количество выбитых кеглей в этом
броске
• score() : int – вызывается в конце игры. Показывает
результат игры
© Luxoft Training 2012
Thursday, December 6, 12
26. A quick design session
Нам нужен
BowlingGame класс
BowlingGame
+ roll(pins : int)
© Luxoft Training 2012
Thursday, December 6, 12
27. A quick design session
BowlingGame Frame
+ roll(pins : int) + score() : int
Игра имеет 10
фреймов
© Luxoft Training 2012
Thursday, December 6, 12
28. A quick design session
BowlingGame Frame Roll
+ roll(pins : int) + score() : int - pins : int
Каждый фрейм
состоит из одного или
двух бросков
© Luxoft Training 2012
Thursday, December 6, 12
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. 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. 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
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
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. Assert First
- (void)testGutterGame
{
STAssertEquals(0, [game score], nil);
}
© Luxoft Training 2012
Thursday, December 6, 12
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. Исправляем ошибки, пишем код
- (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. Следующий тест
- (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. Пишем код
- (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. Убираем дублирование
- (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. Рефакторим
- (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. Проверка на 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. 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. 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. 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. Убираем 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. Пишем код
- (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. Очередная сессия 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. 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. Убираем 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. Пишем код
- (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. 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. 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. Проверка на 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. Пишем код
- (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. 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. 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. 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. Контрольный пример
- (void)testPrefectGame {
[self rollMany:10 times:12];
STAssertEquals(300, [game score], nil);
}
© Luxoft Training 2012
Thursday, December 6, 12
61. Cтратегии запуска тестов
§ IDE
§ Console
§ CocoaPods
§ Maven
§ Test Runner
§ CI
§ File watcher
© Luxoft Training 2012
Thursday, December 6, 12
63. Преимущества от TDD
Меньше ошибок
Проще рефакторить
Документация
Лучший дизайн кода
Быстрее процесс разработки
© Luxoft Training 2012
Thursday, December 6, 12
64. BDD
Behavior Driven Development (BDD)
© Luxoft Training 2012
Thursday, December 6, 12
65. BDD – что это?
Language
© Luxoft Training 2012
Thursday, December 6, 12
67. BDD
DDD-Domain Driven Testing + DSL + GIVEN / WHEN / THEN
Спецификация
Тесты
Документация
© Luxoft Training 2012
Thursday, December 6, 12
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
71. *.feature
# language: ru
Функционал: Авторизация пользователей
Чтобы указывать себя автором снипетов, голосовать за
снипеты и нарабатывать карму, пользователи должны иметь
возможность регистрироваться
Сценарий: Успешная авторизация с указываемыми логином и паролем
Допустим я зарегистрированный пользователь "User12" с паролем "User1212"
И я на странице Авторизация
Если ввожу в поле Логин "User12"
И ввожу в поле Пароль "User1212"
И кликаю кнопку "Войти"
То я должен увидеть уведомление "Добро пожаловать!"
И я должен оказаться на странице Страница пользователя
© Luxoft Training 2012
Thursday, December 6, 12
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