74
© Luxoft Training 2012 Test Driven Development Разработка через тестирование iOS, Bowling Game Kata Ivan Dyachenko <[email protected]> http://uamobile.in.ua/ Thursday, December 6, 12

Tdd objective c

Embed Size (px)

Citation preview

Page 1: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Test Driven Development

Разработка через тестирование

iOS, Bowling Game Kata

Ivan Dyachenko <[email protected]>

http://uamobile.in.ua/

Thursday, December 6, 12

Page 2: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Экстремальное программирование (XP)

Разработка через тестирование (TDD)

Agile Manifesto в 2001 году.

Kent Beck

Thursday, December 6, 12

Page 3: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Extreme Programming Practices

Whole Team

Small Releases

Planning Game

CustomerTests

CodingStandard

SustainablePace

CollectiveOwnership

ContinuousIntegration

Metaphor

Test-DrivenDevelopment

Simple Design

RefactoringPairProgramming

Thursday, December 6, 12

Page 4: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Extreme Programming Practices

Whole Team

Small Releases

Planning Game

CustomerTests

CodingStandard

SustainablePace

CollectiveOwnership

ContinuousIntegration

Metaphor

Test-DrivenDevelopment

Simple Design

RefactoringPairProgramming

Thursday, December 6, 12

Page 5: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

TDD

«Чистый код, который работает»

Thursday, December 6, 12

Page 6: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Мифы TDD

Unit tests == TDD

TDD == 100% coverage

TDD == время * 2

TDD == серебряная пуля

Thursday, December 6, 12

Page 7: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Два простых правила TDD

Удаляем дублирование

Пишем новый код только тогда, когда автоматический код не сработал

2

1

Thursday, December 6, 12

Page 8: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Мантра TDD – “red, green, refactor”

Thursday, December 6, 12

Page 9: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

Thursday, December 6, 12

Page 10: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

1 Напиши тест

Thursday, December 6, 12

Page 11: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

2 Скомпилируй

1 Напиши тест

Thursday, December 6, 12

Page 12: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

2 Скомпилируй

3 Исправь ошибки

1 Напиши тест

Thursday, December 6, 12

Page 13: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

2 Скомпилируй

3 Исправь ошибки

4 Запусти и убедись что тесты упали

1 Напиши тест

Thursday, December 6, 12

Page 14: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

2 Скомпилируй

3 Исправь ошибки

4 Запусти и убедись что тесты упали

5 Напиши код

1 Напиши тест

Thursday, December 6, 12

Page 15: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

2 Скомпилируй

3 Исправь ошибки

4 Запусти и убедись что тесты упали

5 Напиши код

6 Запусти и убедись что тесты прошли

1 Напиши тест

Thursday, December 6, 12

Page 16: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

2 Скомпилируй

3 Исправь ошибки

4 Запусти и убедись что тесты упали

5 Напиши код

7 Рефакторинг

6 Запусти и убедись что тесты прошли

1 Напиши тест

Thursday, December 6, 12

Page 17: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

RED / GREEN / REFACTOR

0 Подумай

2 Скомпилируй

3 Исправь ошибки

4 Запусти и убедись что тесты упали

5 Напиши код

7 Рефакторинг

6 Запусти и убедись что тесты прошли

1 Напиши тест

Повтори

Thursday, December 6, 12

Page 18: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Ошибки TDD

Писать тест который сразу проходит

Писать тест после кода

Писать сразу много тестов

Thursday, December 6, 12

Page 19: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Инструменты

§ OCUnit/SenTestingKit начиная IPhone SDK 2.2

§ GHUnit

§ Google Toolkit for Mac

§ OCMock

§ OCHamcrest

§ Kiwi

§ Cedar

§ …

Thursday, December 6, 12

Page 20: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

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

§ Tests frameworks, bundles, or applications

§ Easy installation

Thursday, December 6, 12

Page 21: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

OCUnit

Asserts

§ STAssertNil

§ STAssertNotNil

§ STAssertTrue

§ STAssertFalse

§ STAssertEquals

§ STAssertEqualObjects

§ STFail

§ ...

TestCase

§ test<Test Case>

§ setUp

§ tearDown

Thursday, December 6, 12

Page 23: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Thursday, December 6, 12

Page 24: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Правила игры в боулинг

Раунды

§ 10 раундов (frame-ов)

§ В одном  frame – 2 броска

§ Цель – выбить кегли (pins)

§ Выигрывает набравший больше всех очков

Подсчет очков

§ 10 сразу (strike). Заканчивается досрочно.10 очков + pins + pins

§ 10 очков со второго броска (spare)10 очков + pins

§ Max 300 очков (12 strikes)

Последний frame

§ 1-й strike – дает возможность бросить еще два раза

§ 2-й spare – дает возможность бросить еще одни раз

Thursday, December 6, 12

Page 25: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Надо написать

BowlingGame

+ roll(pins : int)+ score() : int

Написать класс BowlingGame, который имеет два метода

• roll (pins : int) – вызывается каждый раз когда игрок бросает шар. Pins – количество выбитых кеглей в этом броске

• score() : int – вызывается в конце игры. Показывает результат игры

Thursday, December 6, 12

Page 26: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

A quick design session

BowlingGame

+ roll(pins : int)

Нам нужен BowlingGame класс

Thursday, December 6, 12

Page 27: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

A quick design session

BowlingGame

+ roll(pins : int)

Frame

+ score() : int

Игра имеет 10 фреймов

Thursday, December 6, 12

Page 28: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

A quick design session

BowlingGame

+ roll(pins : int)

Frame

+ score() : int

Roll

- pins : int

Каждый фрейм состоит из одного или

двух бросков

Thursday, December 6, 12

Page 29: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

A quick design session

BowlingGame

+ roll(pins : int)

Frame

+ score() : int

Roll

- pins : int

Tenth FrameПоследний фрейм

содержит два или три броска

Thursday, December 6, 12

Page 30: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

A quick design session

BowlingGame

+ roll(pins : int)

Frame

+ score() : int

Roll

- pins : int

Tenth Frame

Score функция должна пройтись по всем

фреймам и посчитать результат

Thursday, December 6, 12

Page 31: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

A quick design session

BowlingGame

+ roll(pins : int)

Frame

+ score() : int

Roll

- pins : int

Tenth Frame

Strike или Spare зависит от количества выбитых кеглей в

FrameNext Frame

Thursday, December 6, 12

Page 32: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Готовим среду для тестирования

+

Thursday, December 6, 12

Page 33: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Еще раз повторим правила

Раунды

§ 10 раундов (frame-ов)

§ В одном  frame – 2 броска

§ Цель – выбить кегли (pins)

§ Выигрывает набравший больше всех очков

Подсчет очков

§ 10 сразу (strike). Заканчивается досрочно.10 очков + pins + pins

§ 10 очков со второго броска (spare)10 очков + pins

§ Max 300 очков (12 strikes)

Последний frame

§ 1-й strike – дает возможность бросить еще два раза

§ 2-й spare – дает возможность бросить еще одни раз

Thursday, December 6, 12

Page 34: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Пишем код

Thursday, December 6, 12

Page 35: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

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];}

@end

Thursday, December 6, 12

Page 36: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Assert First- (void)testGutterGame{ STAssertEquals(0, [game score], nil);}

Thursday, December 6, 12

Page 37: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

- (void)testGutterGame{ BowlingGame *game = [[BowlingGame alloc] init]; for (int i = 0; i < 20; i++) { [game roll:0]; } STAssertEquals(0, [game score], nil);}

Test First

Пишем тест игнорируя ошибки компилятора

Thursday, December 6, 12

Page 38: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

@implementation BowlingGame

- (void)roll:(int)pins { }

- (int)score { return 0;}

@end

Исправляем ошибки, пишем код

Пишем минимум кода! KISS

- (void)testGutterGame{ BowlingGame *game = [[BowlingGame alloc] init]; for (int i = 0; i < 20; i++) { [game roll:0]; } STAssertEquals(0, [game score], nil);}

Thursday, December 6, 12

Page 39: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Следующий тестТест падает- (void)testAllOne

{ BowlingGame *game = [[BowlingGame alloc] init]; for (int i = 0; i < 20; i++) { [game roll:1]; } STAssertEquals(20, [game score], nil);}

Thursday, December 6, 12

Page 40: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Пишем код

Тест проходит

- (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;}

@end

Thursday, December 6, 12

Page 41: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

- (void)testGutterGame{ int n = 20; int pins = 0; for (int i = 0; i < n; i++) { [game roll:pins]; } STAssertEquals(0, [game score], nil);}

Убираем дублирование

Thursday, December 6, 12

Page 42: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

- (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]; }}

Рефакторим

Thursday, December 6, 12

Page 43: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Проверка на Spare

10 + 3

6

4

3

-

3

Тест падает

- (void)testScoreSpare { [game roll:5]; // spare [game roll:5]; [game roll:3]; [self rollMany:17 times:0]; STAssertEquals(16, [game score], nil);}

Thursday, December 6, 12

Page 44: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

#import "BowlingGame.h"

@implementation BowlingGame

- (void)roll:(int)pins { score += pins;}

- (int)score { return score;}

@end

Design reviewroll – занимается

подсчетом очков, но имя метода не указывает на это

score – не занимается подсчетом очков, хотя имя указывает, что

должна

Thursday, December 6, 12

Page 45: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Redesign Добавляем метод в Ignore

//- (void)testScoreSpare {// [game roll:5]; // spare// [game roll:5];// [game roll:3];// [self rollMany:17 times:0];// STAssertEquals(16, [game score], nil);//}

Thursday, December 6, 12

Page 46: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

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

Thursday, December 6, 12

Page 47: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Убираем Ignore

Тест падает

- (void)testScoreSpare { [game roll:5]; // spare [game roll:5]; [game roll:3]; [self rollMany:17 times:0]; STAssertEquals(16, [game score], nil);}

Thursday, December 6, 12

Page 48: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Пишем код

Хорошая идея! За одним исключением – это не работает

У нас все еще проблемы с дизайном. Очевидно, надо считать очки по фреймам

- (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;}

Thursday, December 6, 12

Page 49: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Очередная сессия Redesign

Добавляем метод в Ignore

//- (void)testScoreSpare {// [game roll:5]; // spare// [game roll:5];// [game roll:3];// [self rollMany:17 times:0];// STAssertEquals(16, [game score], nil);//}

Thursday, December 6, 12

Page 50: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

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;}

Thursday, December 6, 12

Page 51: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Убираем Ignore

Тест падает

- (void)testScoreSpare { [game roll:5]; // spare [game roll:5]; [game roll:3]; [self rollMany:17 times:0]; STAssertEquals(16, [game score], nil);}

Thursday, December 6, 12

Page 52: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

- (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;}

Пишем код

Thursday, December 6, 12

Page 53: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

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;}

Thursday, December 6, 12

Page 54: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

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 { return rolls[ballIndex] + rolls[ballIndex+1] == 10;}

Thursday, December 6, 12

Page 55: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

- (void)testScoreStrike { [game roll:10]; // strike [game roll:3]; [game roll:4]; [self rollMany:16 times:0]; STAssertEquals(24, [game score], nil);}

Проверка на Strike

10 + 3 + 4

10

*

3

4

3 + 4Комментарий в тесте

Thursday, December 6, 12

Page 56: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Пишем код

- (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; } } return score;}

Thursday, December 6, 12

Page 57: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

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;}

Thursday, December 6, 12

Page 58: Tdd objective c

- (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;}

© L

uxof

t Tra

inin

g 20

12

Refactoring

Thursday, December 6, 12

Page 59: Tdd objective c

- (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];}

© L

uxof

t Tra

inin

g 20

12

Refactoring

git checkout bowling-0.8

Thursday, December 6, 12

Page 60: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Контрольный пример

- (void)testPrefectGame { [self rollMany:10 times:12]; STAssertEquals(300, [game score], nil);}

Thursday, December 6, 12

Page 61: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Cтратегии запуска тестов

§ IDE

§ Console

§ CocoaPods

§ Maven

§ Test Runner

§ CI

§ File watcher

Thursday, December 6, 12

Page 62: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Continuous integration

HudsonTeam City

Thursday, December 6, 12

Page 63: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Преимущества от TDD

Меньше ошибок

Проще рефакторить

Документация

Лучший дизайн кода

Быстрее процесс разработки

Thursday, December 6, 12

Page 64: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

BDD

Behavior Driven Development (BDD)

Thursday, December 6, 12

Page 65: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

BDD – что это?

Language

Thursday, December 6, 12

Page 66: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Difference between TDD, BDD & ATDD

TDD ATDD

Thursday, December 6, 12

Page 67: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

BDD

Спецификация

Тесты

Документация

DDD-Domain Driven Testing + DSL + GIVEN / WHEN / THEN

Thursday, December 6, 12

Page 68: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Kiwidescribe(@"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]; [game roll:4]; rollMany(16, 0); [[theValue([game score]) should] equal:theValue(24)]; });

Thursday, December 6, 12

Page 69: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Thursday, December 6, 12

Page 70: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Thursday, December 6, 12

Page 71: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

*.feature# language: ruФункционал: Авторизация пользователей

Чтобы указывать себя автором снипетов, голосовать за снипеты и нарабатывать карму, пользователи должны иметь возможность регистрироваться

Сценарий: Успешная авторизация с указываемыми логином и паролем Допустим я зарегистрированный пользователь "User12" с паролем "User1212" И я на странице Авторизация Если ввожу в поле Логин "User12" И ввожу в поле Пароль "User1212" И кликаю кнопку "Войти" То я должен увидеть уведомление "Добро пожаловать!" И я должен оказаться на странице Страница пользователя

Thursday, December 6, 12

Page 72: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

*.feature

Thursday, December 6, 12

Page 73: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Вопросы ?

Thursday, December 6, 12

Page 74: Tdd objective c

© L

uxof

t Tra

inin

g 20

12

Разработка через тестирование

git clone git://github.com/ivan-dyachenko/Trainings.git

[email protected]

https://github.com/ivan-dyachenko/Trainings

Thursday, December 6, 12