108
TESTING ~ TDD Kalinichev.net

Testing & TDD

Embed Size (px)

Citation preview

Page 1: Testing & TDD

TESTING ~

TDD

Kalinichev.net !

Page 2: Testing & TDD
Page 3: Testing & TDD
Page 4: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Пошло из XP: test-first

Page 5: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Пошло из XP: test-first

• Реже используешь отладчик

Page 6: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Пошло из XP: test-first

• Реже используешь отладчик

• Дизайн еще до реализации

Page 7: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Пошло из XP: test-first

• Реже используешь отладчик

• Дизайн еще до реализации

• Код менее связанный

Page 8: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Пошло из XP: test-first

• Реже используешь отладчик

• Дизайн еще до реализации

• Код менее связанный

• Нет сложной инициализации

Page 9: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Пошло из XP: test-first

• Реже используешь отладчик

• Дизайн еще до реализации

• Код менее связанный

• Нет сложной инициализации

• Интерфейсы четкие и небольшие

Page 10: Testing & TDD

TDD? ЧТО И ОТКУДА?• Пошло из XP: test-first

• Реже используешь отладчик

• Дизайн еще до реализации

• Код менее связанный

• Нет сложной инициализации

• Интерфейсы четкие и небольшие

• Способствует модульному мышлению

!

Page 11: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Нужно привыкнуть

Page 12: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Нужно привыкнуть

• На старте может потребовать больше времени

Page 13: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Нужно привыкнуть

• На старте может потребовать больше времени

•Может возникнуть ложное чувство безопасности

Page 14: Testing & TDD

TDD? ЧТО И ОТКУДА?

• Нужно привыкнуть

• На старте может потребовать больше времени

•Может возникнуть ложное чувство безопасности

• Плохо написанный тест - источник накладных расходов!

Page 15: Testing & TDD

ПЛАН

• Что не так?

• Как хочется?

• Примеры

• Примеры

• Примеры

• Практика

Page 16: Testing & TDD

ЧТО ВЫ МОЖЕТЕ СКАЗАТЬ О ТЕСТАХ?

Page 17: Testing & TDD

Я ненавижу свои тесты

Page 18: Testing & TDD

Я ненавижу свои тесты ... да и чужие

Page 19: Testing & TDD

ПОЧЕМУ?

Page 20: Testing & TDD

•Медленные

Page 21: Testing & TDD

•Медленные

•Хрупкие

Page 22: Testing & TDD

•Медленные

•Хрупкие

•Что еще хуже - Random’но хрупкие

Page 23: Testing & TDD

•Медленные

•Хрупкие

•Что еще хуже - Random’но хрупкие

•Дорогие по деньгам

Page 24: Testing & TDD

•Медленные

•Хрупкие

•Что еще хуже - Random’но хрупкие

•Дорогие по деньгам

•Что еще хуже - по времени

Page 25: Testing & TDD

•Медленные

•Хрупкие

•Что еще хуже - Random’но хрупкие

•Дорогие по деньгам

•Что еще хуже - по времени

•Хрен поймешь - что тестируют?

Page 26: Testing & TDD

•Медленные

•Хрупкие

•Что еще хуже - Random’но хрупкие

•Дорогие по деньгам

•Что еще хуже - по времени

•Хрен поймешь - что тестируют?

•Писать и разбираться в них - одна боль и страдание

Page 27: Testing & TDD

ТАК НЕ ДОЛЖНО БЫТЬ!

Page 28: Testing & TDD

Давайте удалим такие тесты

Page 29: Testing & TDD

Давайте удалим такие тесты- Что все?

Page 30: Testing & TDD

ЦЕЛЬ

Page 31: Testing & TDD

UNIT ТЕСТЫ ДОЖНЫ БЫТЬ

Page 32: Testing & TDD

UNIT ТЕСТЫ ДОЖНЫ БЫТЬ

•Полными

Page 33: Testing & TDD

UNIT ТЕСТЫ ДОЖНЫ БЫТЬ

•Полными

•Стабильными

Page 34: Testing & TDD

UNIT ТЕСТЫ ДОЖНЫ БЫТЬ

•Полными

•Стабильными

•Быстрыми

Page 35: Testing & TDD

UNIT ТЕСТЫ ДОЖНЫ БЫТЬ

•Полными

•Стабильными

•Быстрыми

•Короткими

Page 36: Testing & TDD

КАК ДЕЛА В КОДЕ НАШИХ ПРОЕКТОВ?

Page 37: Testing & TDD

КАК ДЕЛА В КОДЕ НАШИХ ПРОЕКТОВ?

Page 38: Testing & TDD

Давайте подумаем про один какой-нибудь объект...

Page 39: Testing & TDD
Page 40: Testing & TDD

ОБЪЕКТ

•Получает что-то от кого-то

•Отправляет что-то кому-то

•Внутри сам себе покоя не дает

Page 41: Testing & TDD

ВСЕ ЭТО НЕКИЕ СООБЩЕНИЯ

Page 42: Testing & TDD

СООБЩЕНИЯ МОЖНО РАЗБИТЬ НА ДВА ТИПА

Page 43: Testing & TDD

Запросы: что-то возвращают и ничего не меняют

Page 44: Testing & TDD

Запросы: что-то возвращают и ничего не меняют

Команды: ничего не возвращают и что-то меняют

Page 45: Testing & TDD

Запросы: что-то возвращают и ничего не меняют

Команды: ничего не возвращают и что-то меняют

Избавляйтесь от запросов, которые ведут себя как команды

i

Page 46: Testing & TDD

ЧТО И КАК ТЕСТИРОВАТЬ

Запросы Команды

Входящие

Самому себе

Исходящие

Page 47: Testing & TDD

ВХОДЯЩИЕ СООБЩЕНИЯ

Page 48: Testing & TDD

ВХОДЯЩИЕ ЗАПРОСЫ private class Aggregator {! private readonly int[] _args;! public Aggregator(params int[] args) {! _args = args;! }! public int Sum {! get { return _args.Sum(); }! }! }!!

Page 49: Testing & TDD

ВХОДЯЩИЕ ЗАПРОСЫ private class Aggregator {! private readonly int[] _args;! public Aggregator(params int[] args) {! _args = args;! }! public int Sum {! get { return _args.Sum(); }! }! }!! [TestFixture]! public class AggregatorTest {! [Test]! public void Sum() {! var a = new Aggregator(1, 2, 3);! Assert.AreEqual(6, a.Sum);! }! }!

Page 50: Testing & TDD

ВХОДЯЩИЕ ЗАПРОСЫ

Правило 1 !

Тестируем входящие запросы по тому что они возвращают

R

Page 51: Testing & TDD

ВХОДЯЩИЕ ЗАПРОСЫ! private class MyAverage {! public const int MAGIC_COEF = 2;! private readonly int[] _args;! public MyAverage(params int[] args) {! _args = args;! }! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! private int[] ModifyArgs {! get { return _args.Select(a => a * MAGIC_COEF).ToArray(); }! }! }!!

Page 52: Testing & TDD

ВХОДЯЩИЕ ЗАПРОСЫ! private class MyAverage {! public const int MAGIC_COEF = 2;! private readonly int[] _args;! public MyAverage(params int[] args) {! _args = args;! }! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! private int[] ModifyArgs {! get { return _args.Select(a => a * MAGIC_COEF).ToArray(); }! }! }!! [TestFixture]! public class MyAverageTest {! [Test]! public void Calc() {! var avg = new MyAverage(10, 20);! Assert.AreEqual(30, avg.Calc());! // Но, там же еще всего так много: MAGIC_COEF, ModifyArgs.! }! }!

Page 53: Testing & TDD

ВХОДЯЩИЕ ЗАПРОСЫ

Правило 2 !

Тестируем интерфейсы, а не конкретную реализацию

R

Page 54: Testing & TDD

ВХОДЯЩИЕ КОМАНДЫ! private class MyAverage2 : MyAverage {! private int _coef;!! public void SetCoef(int value) {! _coef = value;! }! ! public int CurrentMagicCoef { get { return _coef + MAGIC_COEF; } }! }!!

Page 55: Testing & TDD

ВХОДЯЩИЕ КОМАНДЫ! private class MyAverage2 : MyAverage {! private int _coef;!! public void SetCoef(int value) {! _coef = value;! }! ! public int CurrentMagicCoef { get { return _coef + MAGIC_COEF; } }! }!! [TestFixture]! public class MyAverage2Test {! [Test]! public void SetCoef() {! var avg = new MyAverage2();! avg.SetCoef(1);! Assert.AreEqual(3, avg.CurrentMagicCoef);! }! }!

Page 56: Testing & TDD

ВХОДЯЩИЕ КОМАНДЫ

Правило 3 !

Тестируем входящие команды по ближайшему публичному, побочному эффекту

R

Page 57: Testing & TDD

ЧТО И КАК ТЕСТИРОВАТЬ

Запросы Команды

Входящие Проверка результата

Проверка публичного, побочного эффекта

Самому себе

Исходящие

Page 58: Testing & TDD

СООБЩЕНИЯ САМОМУ СЕБЕ

Page 59: Testing & TDD

ЗАПРОСЫ К СЕБЕ! private class MyAverage4 {! public const int MAGIC_COEF = 2;! private readonly int[] _args;! public MyAverage4(params int[] args) {! _args = args;! }! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! internal int[] ModifyArgs {! get { return _args.Select(a => a * MAGIC_COEF).ToArray(); }! }! }!!!

Page 60: Testing & TDD

ЗАПРОСЫ К СЕБЕ! private class MyAverage4 {! public const int MAGIC_COEF = 2;! private readonly int[] _args;! public MyAverage4(params int[] args) {! _args = args;! }! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! internal int[] ModifyArgs {! get { return _args.Select(a => a * MAGIC_COEF).ToArray(); }! }! }!!!

Page 61: Testing & TDD

ЗАПРОСЫ К СЕБЕ! private class MyAverage4 {! public const int MAGIC_COEF = 2;! private readonly int[] _args;! public MyAverage4(params int[] args) {! _args = args;! }! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! internal int[] ModifyArgs {! get { return _args.Select(a => a * MAGIC_COEF).ToArray(); }! }! }!! [TestFixture]! public class MyAverage4Test {! [Test]! public void ModifyArgs() {! var avg = new MyAverage4(10);! Assert.AreEqual(20, avg.ModifyArgs);! }! }!

Page 62: Testing & TDD

ЗАПРОСЫ К СЕБЕ! private class MyAverage4 {! public const int MAGIC_COEF = 2;! private readonly int[] _args;! public MyAverage4(params int[] args) {! _args = args;! }! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! internal int[] ModifyArgs {! get { return _args.Select(a => a * MAGIC_COEF).ToArray(); }! }! }!! [TestFixture]! public class MyAverage4Test {! [Test]! public void ModifyArgs() {! var avg = new MyAverage4(10);! Assert.AreEqual(20, avg.ModifyArgs);! }! }!

ТАК ДЕЛАТЬ НЕ НАДО

Page 63: Testing & TDD

КОМАНДЫ К СЕБЕ! private class MyAverage5 {! private readonly int[] _args;! internal Aggregator Aggregator;! public MyAverage5(params int[] args) {! _args = args;! }! public int Calc() {! InitAggregator()! return Aggregator.Sum / _args.Length;! }! private void InitAggregator() {! Aggregator = new Aggregator(_args);! }! }!

Page 64: Testing & TDD

КОМАНДЫ К СЕБЕ! private class MyAverage5 {! private readonly int[] _args;! internal Aggregator Aggregator;! public MyAverage5(params int[] args) {! _args = args;! }! public int Calc() {! InitAggregator()! return Aggregator.Sum / _args.Length;! }! private void InitAggregator() {! Aggregator = new Aggregator(_args);! }! }!! [TestFixture]! public class MyAverage5Test {! [Test]! public void Calc() {! var avg = new MyAverage5(10, 20);! Assert.AreEqual(15, avg.Calc());! Assert.IsNotNull(avg.Aggregator);! }! }!

Page 65: Testing & TDD

КОМАНДЫ К СЕБЕ! private class MyAverage5 {! private readonly int[] _args;! internal Aggregator Aggregator;! public MyAverage5(params int[] args) {! _args = args;! }! public int Calc() {! InitAggregator()! return Aggregator.Sum / _args.Length;! }! private void InitAggregator() {! Aggregator = new Aggregator(_args);! }! }!! [TestFixture]! public class MyAverage5Test {! [Test]! public void Calc() {! var avg = new MyAverage5(10, 20);! Assert.AreEqual(15, avg.Calc());! Assert.IsNotNull(avg.Aggregator);! }! }!

Этому тут не место

Page 66: Testing & TDD

КОМАНДЫ К СЕБЕ! private class MyAverage5 {! private readonly int[] _args;! internal Aggregator Aggregator;! public MyAverage5(params int[] args) {! _args = args;! }! public int Calc() {! InitAggregator()! return Aggregator.Sum / _args.Length;! }! private void InitAggregator() {! Aggregator = new Aggregator(_args);! }! }!! [TestFixture]! public class MyAverage5Test {! [Test]! public void Calc() {! var avg = new MyAverage5(10, 20);! Assert.AreEqual(15, avg.Calc());! Assert.IsNotNull(avg.Aggregator);! }! }!

ТАК ДЕЛАТЬ НЕ НАДО

Page 67: Testing & TDD

СООБЩЕНИЯ САМОМУ СЕБЕ

Правило 4 !

Не тестируйте внутренние методы - не проверяйте их результат - не ожидайте их вызова

R

Page 68: Testing & TDD

ЧТО И КАК ТЕСТИРОВАТЬ

Запросы Команды

Входящие Проверка результата

Проверка публичного, побочного эффекта

Самому себе Игнорируем Игнорируем

Исходящие

Page 69: Testing & TDD

ИСХОДЯЩИЕ СООБЩЕНИЯ

Page 70: Testing & TDD

ИСХОДЯЩИЕ ЗАПРОСЫ! public interface IMagicCoef {! int Value { get; }! }! class MagicCoef : IMagicCoef {! public int Value { ! get { return /* Find Magic Value... */ default(int); } }! }!! private class MyAverage6 {! private readonly IMagicCoef _magic;! private readonly int[] _args;! public MyAverage6(IMagicCoef magic = null, params int[] args) {! _magic = magic ?? new MagicCoef();! _args = args;! }! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! private int[] ModifyArgs {! get { return _args.Select(a => a * _magic.Value).ToArray(); }! }! }!

Page 71: Testing & TDD

ИСХОДЯЩИЕ ЗАПРОСЫ! public interface IMagicCoef {! int Value { get; }! }! class MagicCoef : IMagicCoef {! public int Value { ! get { return /* Find Magic Value... */ default(int); } }! }!! private class MyAverage6 {! private readonly IMagicCoef _magic;! private readonly int[] _args;! public MyAverage6(IMagicCoef magic = null, params int[] args) {! _magic = magic ?? new MagicCoef();! _args = args;! }! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! private int[] ModifyArgs {! get { return _args.Select(a => a * _magic.Value).ToArray(); }! }! }!

Page 72: Testing & TDD

ИСХОДЯЩИЕ ЗАПРОСЫ! class MagicCoef : IMagicCoef {! public int Value { ! get { return /* Find Magic Value... */ default(int); } }! }!! private class MyAverage6 {! ...! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! private int[] ModifyArgs {! get { return _args.Select(a => a * _magic.Value).ToArray(); }! }! }!! [TestFixture]! public class MyAverage6Test {! [Test]! public void CallMagic() {! var mock = new Mock<IMagicCoef>();! new MyAverage6(mock.Object, 1, 2).Calc();! mock.Verify(m => m.Value, Times.Exactly(2));! }! }!!

Page 73: Testing & TDD

ИСХОДЯЩИЕ ЗАПРОСЫ! class MagicCoef : IMagicCoef {! public int Value { ! get { return /* Find Magic Value... */ default(int); } }! }!! private class MyAverage6 {! ...! public int Calc() {! return new Aggregator(ModifyArgs).Sum / _args.Length;! }! private int[] ModifyArgs {! get { return _args.Select(a => a * _magic.Value).ToArray(); }! }! }!! [TestFixture]! public class MyAverage6Test {! [Test]! public void CallMagic() {! var mock = new Mock<IMagicCoef>();! new MyAverage6(mock.Object, 1, 2).Calc();! mock.Verify(m => m.Value, Times.Exactly(2));! }! }!!

ТАК ДЕЛАТЬ НЕ НАДО

Page 74: Testing & TDD

ИСХОДЯЩИЕ ЗАПРОСЫ

Правило 5 !

Не тестируйте исходящие запросы - не проверяйте их результат - не ожидайте их вызова

R

Page 75: Testing & TDD

ИСХОДЯЩИЕ ЗАПРОСЫ

Правило 5 !

Не тестируйте исходящие запросы - не проверяйте их результат - не ожидайте их вызова

R

Проверка исходящих запросов добавляет стоимости, но не приносит пользы

i

Page 76: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ!! private class Aggregator7 {! private readonly ILogger _logger;! private readonly int[] _args;! public Aggregator7(ILogger logger = null, params int[] args) {! _logger = logger ?? new LoggerByDb();! _args = args;! }!! public int Sum {! get {! var sum = _args.Sum();! _logger.LogSum(sum);! return sum;! }! }! }!! public interface ILogger {! void LogSum(int sum);! }!! private class LoggerByDb : ILogger {! public void LogSum(int sum) {! // Log sum in data base...! }! }!

Page 77: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ!! private class Aggregator7 {! private readonly ILogger _logger;! private readonly int[] _args;! public Aggregator7(ILogger logger = null, params int[] args) {! _logger = logger ?? new LoggerByDb();! _args = args;! }!! public int Sum {! get {! var sum = _args.Sum();! _logger.LogSum(sum);! return sum;! }! }! }!! public interface ILogger {! void LogSum(int sum);! }!! private class LoggerByDb : ILogger {! public void LogSum(int sum) {! // Log sum in data base...! }! }!

Page 78: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ! [TestFixture]! public class Aggregator7Test {! [Test]! public void LogTest() {! var sum = new Aggregator7(new LoggerByDb(), 1, 2, 3).Sum;! var logSum = GetLogsFromDb();! Assert.AreEqual(sum, logSum);! }!! private int GetLogsFromDb() {! // select sum from logtable limit 1;! return default (int);! }! }!

Page 79: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ! [TestFixture]! public class Aggregator7Test {! [Test]! public void LogTest() {! var sum = new Aggregator7(new LoggerByDb(), 1, 2, 3).Sum;! var logSum = GetLogsFromDb();! Assert.AreEqual(sum, logSum);! }!! private int GetLogsFromDb() {! // select sum from logtable limit 1;! return default (int);! }! }!

Это зависимость от «далеких» эффектов и конкретной реализации. Замедляет тесты.

i

Page 80: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ! [TestFixture]! public class Aggregator7Test {! [Test]! public void LogTest() {! var sum = new Aggregator7(new LoggerByDb(), 1, 2, 3).Sum;! var logSum = GetLogsFromDb();! Assert.AreEqual(sum, logSum);! }!! private int GetLogsFromDb() {! // select sum from logtable limit 1;! return default (int);! }! }!

Это зависимость от «далеких» эффектов и конкретной реализации. Замедляет тесты.

Главное - не имеет отношение к ответственности класса.

i

Page 81: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ! [TestFixture]! public class Aggregator7Test {! [Test]! public void LogTest() {! var sum = new Aggregator7(new LoggerByDb(), 1, 2, 3).Sum;! var logSum = GetLogsFromDb();! Assert.AreEqual(sum, logSum);! }!! private int GetLogsFromDb() {! // select sum from logtable limit 1;! return default (int);! }! }!

Это зависимость от «далеких» эффектов и конкретной реализации. Замедляет тесты.

Главное - не имеет отношение к ответственности класса.

i

ТАК ДЕЛАТЬ НЕ НАДО

Page 82: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ! [TestFixture]! public class Aggregator7Test {! // Anti pattern! [Test]! public void BadLogTest() {! var sum = new Aggregator7(new LoggerByDb(), 1, 2, 3).Sum;! var logSum = GetLogsFromDb();! Assert.AreEqual(sum, logSum);! }!! private int GetLogsFromDb() {! // select sum from logtable limit 1;! return default (int);! }!! // Good! [Test]! public void GoodLogTest() {! var m = new Mock<ILogger>();! var res = new Aggregator7(m.Object, 1, 2, 3).Sum;! m.Verify(e => e.LogSum(6), Times.Once());! }! }!

Page 83: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ! [TestFixture]! public class Aggregator7Test {! // Anti pattern! [Test]! public void BadLogTest() {! var sum = new Aggregator7(new LoggerByDb(), 1, 2, 3).Sum;! var logSum = GetLogsFromDb();! Assert.AreEqual(sum, logSum);! }!! private int GetLogsFromDb() {! // select sum from logtable limit 1;! return default (int);! }!! // Good! [Test]! public void GoodLogTest() {! var m = new Mock<ILogger>();! var res = new Aggregator7(m.Object, 1, 2, 3).Sum;! m.Verify(e => e.LogSum(6), Times.Once());! }! }!

Ответственность класса - отправить командуi

Page 84: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ

Правило 6 !

Проверяйте отправку исходящих команд с определенными параметрами, но не более того

R

Page 85: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ

Правило 6 !

Проверяйте отправку исходящих команд с определенными параметрами, но не более того

R

НО...

Page 86: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ

Что будет если интерфейс ILogger захочется изменить?

Page 87: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ

Что будет если интерфейс ILogger захочется изменить?

БОЛЬ И СТРАДАНИЕ

Page 88: Testing & TDD

ИСХОДЯЩИЕ КОМАНДЫ

Что будет если интерфейс ILogger захочется изменить?

БОЛЬ И СТРАДАНИЕ

Создавайте «тоникие» интерфейсы. Соблюдайте контракты

i

Page 89: Testing & TDD

ЧТО И КАК ТЕСТИРОВАТЬ

Запросы Команды

Входящие Проверка результата

Проверка публичного, побочного эффекта

Самому себе Игнорируем Игнорируем

Исходящие Игнорируем Ожидание вызова

Page 90: Testing & TDD

ИТОГО

Page 91: Testing & TDD

БУДЬТЕ МИНИМАЛИСТОМ

Page 92: Testing & TDD

БУДЬТЕ МИНИМАЛИСТОМв хорошем смысле

Page 93: Testing & TDD

ТЕСТИРУЙТЕ ВСЕ

Page 94: Testing & TDD

ТЕСТИРУЙТЕ ВСЕ

Только ОДИН раз

Page 95: Testing & TDD

НАСТАИВАЙТЕ НА ПРОСТОТЕ

Page 96: Testing & TDD
Page 97: Testing & TDD
Page 98: Testing & TDD

У каждого есть возможность сказать:

Page 99: Testing & TDD

У каждого есть возможность сказать:

«Я обожаю свои тесты»

Page 100: Testing & TDD

ВОПРОСЫ

Page 101: Testing & TDD

TDD

Page 102: Testing & TDD

Внимание! Очень сложно, но крайне важно

Page 103: Testing & TDD
Page 104: Testing & TDD

... это всё

Page 105: Testing & TDD

ВОПРОСЫ

Page 106: Testing & TDD

ССЫЛКИ

• http://en.wikipedia.org/wiki/Test-driven_development

• https://speakerdeck.com/skmetz/magic-tricks-of-testing-railsconf

Page 107: Testing & TDD

Практическая часть

Page 108: Testing & TDD

ВОПРОСЫ