Каждые несколько месяцев
Предполагая, что вы находитесь в одной лодке (возможно, вы собираетесь в группу пользователей, в которой вы впервые будете выполнять ката), вам может быть интересно, чего ожидать, когда вы туда доберетесь.
Давайте сначала определим некоторые термины
Так что же такое юнит-тестирование, TDD, красно-зелёный и т. Д., И прочие глупости, которые я только что упомянул?
(Уровень опыта у всех разный, поэтому не стесняйтесь пропускать все, что вам уже знакомо.)
Что такое юнит тест?
Когда вы создаете программу, вы должны думать о том, как вы узнаете, что это правильно, когда это будет сделано. Как вы убедитесь, что он делает то, что должен делать, * до того, как он поступит в производство?
Вы можете проверить это вручную . Это то, что мы обычно делаем с курсовой работой в школе ... запустите ее одним входом и посмотрите, что получится. Запустите его с другими входами и посмотрите, что произойдет. Пройдите по коду, пытаясь выяснить, что происходит. Но поскольку ваша программа растет от нескольких методов до тысяч методов в сотнях классов, это быстро становится невозможным.
Итак, мы включили компьютер в работу, тестируя себя. Эти автоматизированные тесты бывают самых разных типов - от тестирования одного компонента до полного тестирования всей системы, тестирования только пользовательского интерфейса, нагрузочного тестирования и т. Д.
Основным видом автоматизированного тестирования является модульное тестирование . Вы тестируете одну функциональность одним методом. Вы полностью управляете средой вокруг этого метода, чтобы ваш тест не попал случайно ни в какие внешние ресурсы, такие как база данных, сетевой диск или даже локальный диск.
Если возможно, что база данных выйдет из строя, или сеть станет недоступной, или случайно попытается записать в несанкционированное место на диске, ваш тест может завершиться неудачей по независящей от вас причине, и это бесполезно. Есть еще одна концепция, называемая
Модульные тесты и изоляция идут рука об руку. Вы тестируете один метод с конкретными, известными входными данными и проверяете, что выходные данные точно соответствуют вашим ожиданиям.
Давайте рассмотрим пример ...
Вам было поручено создать калькулятор, и первая функция - это возможность «Разделить». Метод берет два числа, делит их и возвращает частное. Новаторский, верно?
Вы можете начать с метода, подобного следующему (все мои примеры на C #, но, надеюсь, очевидно, что это делает, даже если вы не знакомы с C #), затем запустить его и вручную проверить некоторые числа. Все, что вы бросаете в это, кажется, работает хорошо, поэтому вы называете это днем. Ну, спасибо за чтение, хорошего!
… О, подождите, пропустите 6 месяцев вперед, вы переходите к другим проектам, и пользователи решили, что им тоже нужно умножиться (это требует). Кроме того, в то же время, добавление и вычитание были реализованы кем-то другим, и теперь код периодически выдает неожиданные результаты и взрывается во время выполнения. Время, чтобы вручную запустить номера снова. А еще лучше - вручную запускать числа по всем методам, каждый раз, когда кто-то их строит! (Это смешно. Не делайте этого вручную.)
Дело в том, что метод «Разделить» сейчас терпит неудачу, и никто не знает точно, почему, или даже когда он мог начаться. Ваши пользователи любят складывать и вычитать, но они редко делятся. Вся ваша прекрасная работа, недооцененная. :п
Это глупый пример, но в более широком масштабе мы сталкиваемся с ним все время, когда мы разрабатываем большие приложения, в которые десятки программистов внесли свой вклад в течение ряда лет.
Пришло время начать ** автоматизацию ваших тестов **. Вы можете добавить второй класс, полный методов, которые создают экземпляр Calculator, пропускают через него несколько чисел и возвращают значение, указывающее, совпадают ли ожидаемый ответ и фактический ответ. Следующие два «тестовых» метода возвращают true, если ответы совпадают; ложь в противном случае.
Вы даже можете переместить этот класс в его собственный проект и использовать магию отражения для запуска всех тестов в своем классе тестов , проверки возвращаемых значений и отображения списка неудачных тестов.
Вот консольное приложение, которое делает именно это, но может быть трудно следовать, если вы не знакомы с C #, и, конечно, вы не рекомендовали запускать тесты, поэтому не тратьте на это слишком много времени.
На втором снимке экрана я изменил свои входные данные для получения неверных результатов, поэтому тесты не пройдены.
Так что это лучше, чем руководство, но все же смешно.
Вместо этого вы должны использовать зрелые инструменты тестирования, такие как
Я остановлюсь там сейчас. Надеюсь, это даст вам хотя бы начальное представление о том, что такое юнит-тест. Просто помните ... маленький, изолированный, тестирует одну вещь за один раз, под строгим контролем. (
Что такое TDD и Red-Green-Refactor?
В последнем разделе мы сначала написали метод «Разделить», а затем написали тесты, чтобы проверить его намного позже. Это распространено в устаревшем коде, который изначально был написан без каких-либо тестов.
TDD, или разработка, управляемая тестами, решает эту проблему. Мы пишем тесты * перед * написанием кода. По сути, тесты должны вести программу, указав, что должен делать код.
Давайте снова определим наш метод, но этого достаточно для компиляции кода. Он вообще не учитывает входные данные и, конечно, не реализован правильно.
Теперь мы напишем тест, в котором будет точно указано, что мы ожидаем от этого кода . Теперь я собираюсь перейти на синтаксис NUnit, который должен быть достаточно простым, чтобы следовать, но с большей вероятностью будет соответствовать тому, что вы могли бы увидеть, когда проводите собственное тестирование. NUnit доступен в Visual Studio через NuGet.
Конечно, будучи программистами, мы зацикливаемся на каждой детали, включая то, как мы называем наши тесты. Существуют разные мнения, и вы можете просмотреть несколько
Не стесняйтесь оставлять комментарии ниже, если вы хотите получить разъяснения по любому вопросу.
Пара быстрых заметок, касающихся приведенного выше кода ...
Метод, помеченный атрибутом «SetUp», запускается перед каждым тестом. Создавая новый экземпляр Calculator перед каждым тестом, мы изолируем наши тесты друг от друга (помните, изоляция хороша - мы не хотим, чтобы один тест изменял некоторые значения в одиночном экземпляре Calculator, а затем следующий тест не выполнялся из-за эти измененные значения).
Кроме того, методы больше не возвращают значение. Класс «Assert» и его методы фиксируют результаты теста и сообщают нам об этом. Большинство тестирующих библиотек имеют встроенные аналогичные методы.
Вышесказанное можно еще больше сократить в NUnit, используя атрибут «TestCase» для объединения похожих тестов. Это не имеет отношения к обсуждению TDD, но я включу его сюда, если вам интересно. Метод теста был обновлен, чтобы принимать параметры, которые мы передаем при запуске тестов.
Термин «красный-зеленый-рефактор» тесно связан с TDD.
Когда мы впервые запустим наши модульные тесты, тесты пройдут неудачно. Метод «Разделить» возвращает 0 во всех случаях, поэтому исходное состояние наших тестов - красный. Обратите внимание, как NUnit сообщает, каковы были ожидаемые и фактические значения, а также предоставляет трассировку стека и некоторую другую полезную информацию.
Теперь мы можем исправить оригинальный метод «Разделить», изменив его так, чтобы он снова возвращал дивиденд / делитель ; Теперь тесты проходят (и отображаются зелёным цветом):
Хорошо, теперь новое требование приходит от пользователей. Если коэффициент отрицательный, сделайте его положительным, прежде чем возвращать его. Это странно, но, к счастью, вы тот тип, который плывет по течению.
Давайте напишем еще один тест, чтобы указать ожидаемое поведение (-10 / 5 должно возвращать 2, а не -2) и посмотрим, как провалились первые два теста:
Теперь мы исправим код, чтобы тесты снова прошли (зеленый).
Часть рефакторинга может прийти в любой момент, когда наши тесты проходят.
Когда мы уверены, что наш код работает должным образом (потому что наши тесты пройдены), мы можем реорганизовать код по своему усмотрению.
Замените приведенный выше код на предоставляемую .NET функцию Math.Abs и снова запустите тесты. Они проходят, поэтому изменения ничего не сломали.
Все продолжается таким образом, когда вы пишете тесты, в которых указывается, что должна делать программа, они, скорее всего, потерпят неудачу, а затем вы исправляете код, чтобы тесты прошли.
Я пройду еще один.
Теперь кто-то приходит и говорит: «Ух ты, посмотри, что произойдет, если я разделю на 0!» И некоторые котята попадают в закрученный вихрь. Симпатичные. Действительно неудачно.
Пользователи решают, что они не хотят генерировать исключение. Они хотят вернуть 0, когда делитель равен 0, независимо от того, какой это дивиденд. Нам нужен еще один тест.
Проверьте «сообщение» выше. Новые тесты не прошли (я свернул прохождение тестов), потому что оригинальный метод вызвал DivideByZeroException.
Время снова стать зеленым. Мы * можем * поймать это конкретное исключение, но (по крайней мере, в мире .NET) исключения являются дорогостоящими, и лучше по возможности их предотвратить. В конце концов, если вы можете предвидеть условие и код вокруг него, это не так уж и исключительно.
Запустите тесты снова ... хорошо идти! Теперь, если вы хотите, вы можете вернуться и изменить код, возможно, вместо этого перехватить DivideByZeroException, и тесты все равно пройдут, сообщая, что вы ничего не сломали.
Ясно как грязь? Если вам нужны разъяснения, оставьте комментарий ниже! (
Что такое парное программирование?
Парное программирование - это то, на что это похоже. Одна голова хорошо, а две - лучше.
Если вы когда-либо обращались за помощью к коллеге-программисту, а затем вы оба сидели вместе, решая проблему, вы запрограммировали пару. Есть два способа, которыми люди видят это:
Как это все сочетается с Code Katas?
Когда вы объединяетесь во время ката кода, вы учитесь друг у друга и обсуждаете проблемы по мере их возникновения.
В основном вы будете «пинг-понг» взад и вперед, следуя схеме, подобной следующей:
Видите ли, вы на самом деле не стремитесь «завершить» ката, хотя с некоторыми более короткими вы наверняка это сделаете. Вы больше сосредоточены на следовании дисциплинированному процессу:
Конечно. Рад, что ты спросил.
Популярным (и коротким) является ката Fizz Buzz: (есть еще много на
Шаг 1: вернуть тот же номер
Нам нужен метод, который принимает число и (пока) выплевывает его обратно. Давайте начнем с базового метода, который принимает число и выводит пустую строку, поэтому мы можем скомпилировать.
Давайте просто проверим 1 и 2, чтобы начать. Конечно, это не получается, потому что мы всегда возвращаем пустую строку:
Теперь вы передадите клавиатуру своей паре, чтобы исправить код и выполнить тест.
Шаг 2: верните «Fizz» для кратных 3
Второе требование - напечатать «Fizz» для кратных 3. Ваш партнер может создать несколько тестов или с помощью инструмента, такого как NUnit, использовать атрибут TestCase. Запустите новый тест и посмотрите, как он провалится. В конце концов, мы все равно возвращаем одно и то же число, несмотря ни на что.
Чтобы это произошло, вы можете сделать что-то глупое, например, вернуть «Fizz» точно для указанных входных данных. Или используйте более практичный подход, который обрабатывает любое число, кратное 3. Всегда выполняйте тесты снова, когда закончите, чтобы убедиться, что они пройдены.
Шаг 3: верните «Buzz» для кратных 5
Теперь вы пишете следующий тест, показывающий, что кратные 5 возвращают «Buzz», и снова проходите клавиатуру.
Тест не пройден, и ваша пара исправляет его очень похоже на предыдущее требование.
Шаг 4: верните «FizzBuzz» для кратных 15
Последнее требование, и ваш партнер пишет тест для него .., кратный 3 * и * 5:
Ваш ход, чтобы закончить ката, и вы делаете это, проверяя на кратные 15:
Запустите тесты еще раз и убедитесь, что все они пройдены. Вот полный набор тестов, которые мы проводим.
Хотя я думаю, что приведенных выше тестов достаточно, вы можете проверить каждое значение, просто чтобы быть уверенным:
Последние мысли
На этом этапе мы могли бы снова провести рефакторинг, поскольку все тесты проходят успешно. Я не думаю, что есть еще много чего сделать. Ты видишь что-нибудь, что я пропустил? Опечатки?
Я использовал C #, потому что я наиболее знаком с ним, но вы могли бы соединиться с кем-то, кто знает другой язык. Я пару раз делал пару для создания каты в Ruby - никогда не повредит изучать что-то новое (и встречать кого-то нового!) И видеть, как другие программисты / языки подходят к тестированию.
Там нет давления бизнес-требований или сроков ... просто учиться у других и делиться знаниями. И, может быть, посмеяться, когда время истекло, и вы понимаете, что пришли к отвратительному решению (не то, что * такое * когда-либо случается).
Спасибо, что прочитали это далеко ... Я надеюсь, что вы узнали что-то новое или интересное.
Мысли? У вас есть вопросы или что-то добавить? Позвольте мне знать в комментариях ниже!
Источник:
Ссылка скрыта от гостей
выполняет код kata du jour. Все объединяются и используют общие практики, такие как TDD и красно-зеленый-рефакторинг. Пару недель назад я был партнером с кем-то, кто раньше этого не делал, поэтому у меня была возможность поговорить с ним через это.Предполагая, что вы находитесь в одной лодке (возможно, вы собираетесь в группу пользователей, в которой вы впервые будете выполнять ката), вам может быть интересно, чего ожидать, когда вы туда доберетесь.
Давайте сначала определим некоторые термины
Так что же такое юнит-тестирование, TDD, красно-зелёный и т. Д., И прочие глупости, которые я только что упомянул?
(Уровень опыта у всех разный, поэтому не стесняйтесь пропускать все, что вам уже знакомо.)
Что такое юнит тест?
Когда вы создаете программу, вы должны думать о том, как вы узнаете, что это правильно, когда это будет сделано. Как вы убедитесь, что он делает то, что должен делать, * до того, как он поступит в производство?
Вы можете проверить это вручную . Это то, что мы обычно делаем с курсовой работой в школе ... запустите ее одним входом и посмотрите, что получится. Запустите его с другими входами и посмотрите, что произойдет. Пройдите по коду, пытаясь выяснить, что происходит. Но поскольку ваша программа растет от нескольких методов до тысяч методов в сотнях классов, это быстро становится невозможным.
Итак, мы включили компьютер в работу, тестируя себя. Эти автоматизированные тесты бывают самых разных типов - от тестирования одного компонента до полного тестирования всей системы, тестирования только пользовательского интерфейса, нагрузочного тестирования и т. Д.
Основным видом автоматизированного тестирования является модульное тестирование . Вы тестируете одну функциональность одним методом. Вы полностью управляете средой вокруг этого метода, чтобы ваш тест не попал случайно ни в какие внешние ресурсы, такие как база данных, сетевой диск или даже локальный диск.
Если возможно, что база данных выйдет из строя, или сеть станет недоступной, или случайно попытается записать в несанкционированное место на диске, ваш тест может завершиться неудачей по независящей от вас причине, и это бесполезно. Есть еще одна концепция, называемая
Ссылка скрыта от гостей
которая помогает избежать этих ловушек, но это слишком много для этого поста.Модульные тесты и изоляция идут рука об руку. Вы тестируете один метод с конкретными, известными входными данными и проверяете, что выходные данные точно соответствуют вашим ожиданиям.
Давайте рассмотрим пример ...
Вам было поручено создать калькулятор, и первая функция - это возможность «Разделить». Метод берет два числа, делит их и возвращает частное. Новаторский, верно?
Вы можете начать с метода, подобного следующему (все мои примеры на C #, но, надеюсь, очевидно, что это делает, даже если вы не знакомы с C #), затем запустить его и вручную проверить некоторые числа. Все, что вы бросаете в это, кажется, работает хорошо, поэтому вы называете это днем. Ну, спасибо за чтение, хорошего!
Код:
public class Calculator
{
public decimal Divide(decimal dividend, decimal divisor)
{
return dividend / divisor;
}
}
… О, подождите, пропустите 6 месяцев вперед, вы переходите к другим проектам, и пользователи решили, что им тоже нужно умножиться (это требует). Кроме того, в то же время, добавление и вычитание были реализованы кем-то другим, и теперь код периодически выдает неожиданные результаты и взрывается во время выполнения. Время, чтобы вручную запустить номера снова. А еще лучше - вручную запускать числа по всем методам, каждый раз, когда кто-то их строит! (Это смешно. Не делайте этого вручную.)
Дело в том, что метод «Разделить» сейчас терпит неудачу, и никто не знает точно, почему, или даже когда он мог начаться. Ваши пользователи любят складывать и вычитать, но они редко делятся. Вся ваша прекрасная работа, недооцененная. :п
Это глупый пример, но в более широком масштабе мы сталкиваемся с ним все время, когда мы разрабатываем большие приложения, в которые десятки программистов внесли свой вклад в течение ряда лет.
Пришло время начать ** автоматизацию ваших тестов **. Вы можете добавить второй класс, полный методов, которые создают экземпляр Calculator, пропускают через него несколько чисел и возвращают значение, указывающее, совпадают ли ожидаемый ответ и фактический ответ. Следующие два «тестовых» метода возвращают true, если ответы совпадают; ложь в противном случае.
Код:
public class CalculatorTests
{
Calculator c = new Calculator();
public bool Is6DividedBy3EqualTo2()
{
var quotient = c.Divide(6, 3);
if (quotient == 2)
return true;
else
return false;
}
public bool Is9DividedBy2EqualTo4Point5()
{
var quotient = c.Divide(9, 2);
if (quotient == 4.5m)
return true;
else
return false;
}
}
Вы даже можете переместить этот класс в его собственный проект и использовать магию отражения для запуска всех тестов в своем классе тестов , проверки возвращаемых значений и отображения списка неудачных тестов.
Вот консольное приложение, которое делает именно это, но может быть трудно следовать, если вы не знакомы с C #, и, конечно, вы не рекомендовали запускать тесты, поэтому не тратьте на это слишком много времени.
Код:
public class Program
{
public static void Main()
{
var cTests = new CalculatorTests();
var failedTests = new List<string>();
// using reflection, run every test method and record the names of those methods that fail
foreach (var m in typeof (CalculatorTests).GetMethods()
.Where(m => m.DeclaringType != typeof (object)))
{
if (Convert.ToBoolean(m.Invoke(cTests, null)) != true)
failedTests.Add(m.Name);
}
// display all the failed tests, or a message that everything passed
if (failedTests.Any())
Console.WriteLine("Failed Tests: \r\n\r\n{0}", string.Join("\r\n", failedTests));
else
Console.WriteLine("All tests passed!");
Console.ReadLine();
}
}
На втором снимке экрана я изменил свои входные данные для получения неверных результатов, поэтому тесты не пройдены.
Так что это лучше, чем руководство, но все же смешно.
Вместо этого вы должны использовать зрелые инструменты тестирования, такие как
Ссылка скрыта от гостей
и
Ссылка скрыта от гостей
(языки .NET, такие как C #, F #, VB.NET),
Ссылка скрыта от гостей
(Java),
Ссылка скрыта от гостей
(Ruby) и т. Д. Существуют платформы для различных типов тестов почти в каждом язык, и они облегчают выполнение тестов (по отдельности или все сразу), и расскажут вам больше о том, что конкретно может пойти не так.Я остановлюсь там сейчас. Надеюсь, это даст вам хотя бы начальное представление о том, что такое юнит-тест. Просто помните ... маленький, изолированный, тестирует одну вещь за один раз, под строгим контролем. (
Ссылка скрыта от гостей
)Что такое TDD и Red-Green-Refactor?
В последнем разделе мы сначала написали метод «Разделить», а затем написали тесты, чтобы проверить его намного позже. Это распространено в устаревшем коде, который изначально был написан без каких-либо тестов.
TDD, или разработка, управляемая тестами, решает эту проблему. Мы пишем тесты * перед * написанием кода. По сути, тесты должны вести программу, указав, что должен делать код.
Давайте снова определим наш метод, но этого достаточно для компиляции кода. Он вообще не учитывает входные данные и, конечно, не реализован правильно.
Код:
public decimal Divide(decimal dividend, decimal divisor)
{
return 0;
}
Теперь мы напишем тест, в котором будет точно указано, что мы ожидаем от этого кода . Теперь я собираюсь перейти на синтаксис NUnit, который должен быть достаточно простым, чтобы следовать, но с большей вероятностью будет соответствовать тому, что вы могли бы увидеть, когда проводите собственное тестирование. NUnit доступен в Visual Studio через NuGet.
Конечно, будучи программистами, мы зацикливаемся на каждой детали, включая то, как мы называем наши тесты. Существуют разные мнения, и вы можете просмотреть несколько
Ссылка скрыта от гостей
и
Ссылка скрыта от гостей
, но я буду придерживаться соглашения об именах, которое определяет, что мы тестируем, ожидаемый результат и когда этот результат должен произойти (aka MethodName_ExpectedBehavior_StateUnderTest во второй статье, указанной выше) ,
Код:
[TestFixture]
public class CalculatorTests
{
Calculator c;
[SetUp]
public void Setup()
{
c = new Calculator();
}
[Test]
public void Divide_Returns2_WhenDividing6By3()
{
var quotient = c.Divide(6, 3);
Assert.IsTrue(quotient == 2);
}
[Test]
public void Divide_Returns4_5_WhenDividing9By2()
{
var quotient = c.Divide(9, 2);
Assert.IsTrue(quotient == 4.5m);
}
}
Не стесняйтесь оставлять комментарии ниже, если вы хотите получить разъяснения по любому вопросу.
Пара быстрых заметок, касающихся приведенного выше кода ...
Метод, помеченный атрибутом «SetUp», запускается перед каждым тестом. Создавая новый экземпляр Calculator перед каждым тестом, мы изолируем наши тесты друг от друга (помните, изоляция хороша - мы не хотим, чтобы один тест изменял некоторые значения в одиночном экземпляре Calculator, а затем следующий тест не выполнялся из-за эти измененные значения).
Кроме того, методы больше не возвращают значение. Класс «Assert» и его методы фиксируют результаты теста и сообщают нам об этом. Большинство тестирующих библиотек имеют встроенные аналогичные методы.
Вышесказанное можно еще больше сократить в NUnit, используя атрибут «TestCase» для объединения похожих тестов. Это не имеет отношения к обсуждению TDD, но я включу его сюда, если вам интересно. Метод теста был обновлен, чтобы принимать параметры, которые мы передаем при запуске тестов.
Код:
[TestCase(6, 3, 2, Description = "6 / 3 = 2")]
[TestCase(9, 2, 4.5, Description = "9 / 2 = 4.5")]
public void Divide_ReturnsExpectedQuotient(decimal dividend, decimal divisor, decimal expectedQuotient)
{
var actualQuotient = c.Divide(dividend, divisor);
Assert.AreEqual(expectedQuotient, actualQuotient);
}
Термин «красный-зеленый-рефактор» тесно связан с TDD.
Когда мы впервые запустим наши модульные тесты, тесты пройдут неудачно. Метод «Разделить» возвращает 0 во всех случаях, поэтому исходное состояние наших тестов - красный. Обратите внимание, как NUnit сообщает, каковы были ожидаемые и фактические значения, а также предоставляет трассировку стека и некоторую другую полезную информацию.
Теперь мы можем исправить оригинальный метод «Разделить», изменив его так, чтобы он снова возвращал дивиденд / делитель ; Теперь тесты проходят (и отображаются зелёным цветом):
Хорошо, теперь новое требование приходит от пользователей. Если коэффициент отрицательный, сделайте его положительным, прежде чем возвращать его. Это странно, но, к счастью, вы тот тип, который плывет по течению.
Давайте напишем еще один тест, чтобы указать ожидаемое поведение (-10 / 5 должно возвращать 2, а не -2) и посмотрим, как провалились первые два теста:
Код:
[TestCase(-10, 5, 2, Description = "-10 / 5 = 2")]
[TestCase( 10, -5, 2, Description = "10 / -5 = 2")]
[TestCase(-10, -5, 2, Description = "10 / 5 = 2")]
public void Divide_ReturnsPositiveQuotient_WhenInput(decimal dividend, decimal divisor, decimal expectedQuotient)
{
var actualQuotient = c.Divide(dividend, divisor);
Assert.GreaterOrEqual(actualQuotient, 0);
}
Теперь мы исправим код, чтобы тесты снова прошли (зеленый).
Код:
public decimal Divide(decimal dividend, decimal divisor)
{
// make negative dividends positive
if (dividend < 0)
dividend = -dividend;
// make negative divisors positive
if (divisor < 0)
divisor = -divisor;
return dividend / divisor;
}
Часть рефакторинга может прийти в любой момент, когда наши тесты проходят.
Когда мы уверены, что наш код работает должным образом (потому что наши тесты пройдены), мы можем реорганизовать код по своему усмотрению.
Замените приведенный выше код на предоставляемую .NET функцию Math.Abs и снова запустите тесты. Они проходят, поэтому изменения ничего не сломали.
Код:
public decimal Divide(decimal dividend, decimal divisor)
{
return Math.Abs(dividend / divisor);
}
Все продолжается таким образом, когда вы пишете тесты, в которых указывается, что должна делать программа, они, скорее всего, потерпят неудачу, а затем вы исправляете код, чтобы тесты прошли.
Я пройду еще один.
Теперь кто-то приходит и говорит: «Ух ты, посмотри, что произойдет, если я разделю на 0!» И некоторые котята попадают в закрученный вихрь. Симпатичные. Действительно неудачно.
Пользователи решают, что они не хотят генерировать исключение. Они хотят вернуть 0, когда делитель равен 0, независимо от того, какой это дивиденд. Нам нужен еще один тест.
Код:
[TestCase(5, Description = "5 / 0 = 0")]
[TestCase(0, Description = "0 / 0 = 0")]
[TestCase(-5, Description = "-5 / 0 = 0")]
[Test]
public void Divide_ReturnsZero_WhenDivisorIsZero(decimal input)
{
var actualQuotient = c.Divide(input, 0);
Assert.AreEqual(0, actualQuotient);
}
Проверьте «сообщение» выше. Новые тесты не прошли (я свернул прохождение тестов), потому что оригинальный метод вызвал DivideByZeroException.
Время снова стать зеленым. Мы * можем * поймать это конкретное исключение, но (по крайней мере, в мире .NET) исключения являются дорогостоящими, и лучше по возможности их предотвратить. В конце концов, если вы можете предвидеть условие и код вокруг него, это не так уж и исключительно.
Код:
public decimal Divide(decimal dividend, decimal divisor)
{
if (divisor == 0)
return 0;
return Math.Abs(dividend / divisor);
}
Запустите тесты снова ... хорошо идти! Теперь, если вы хотите, вы можете вернуться и изменить код, возможно, вместо этого перехватить DivideByZeroException, и тесты все равно пройдут, сообщая, что вы ничего не сломали.
Ясно как грязь? Если вам нужны разъяснения, оставьте комментарий ниже! (
Ссылка скрыта от гостей
.)Что такое парное программирование?
Парное программирование - это то, на что это похоже. Одна голова хорошо, а две - лучше.
Если вы когда-либо обращались за помощью к коллеге-программисту, а затем вы оба сидели вместе, решая проблему, вы запрограммировали пару. Есть два способа, которыми люди видят это:
- Некоторые люди считают это пустой тратой ресурсов - две зарплаты с половиной выработки
- Другие люди видят в этом совместную работу - вместо того, чтобы два человека застряли на отдельных задачах или отвлеклись на последние видеофильмы о кошках, они могут обмениваться идеями друг с другом и продолжать двигаться вперед.
Как это все сочетается с Code Katas?
Когда вы объединяетесь во время ката кода, вы учитесь друг у друга и обсуждаете проблемы по мере их возникновения.
В основном вы будете «пинг-понг» взад и вперед, следуя схеме, подобной следующей:
- Вы пишете модульный тест, который не проходит ( красный ), а затем передаете контроль над клавиатурой своему партнеру.
- Она изменяет код, чтобы сделать ваш тест успешным ( зеленый ), затем пишет свой собственный модульный тест (чтобы указать, что программа должна делать дальше), который также не проходит ( снова красный ). Она передает вам контроль.
- Вы пишете больше кода для прохождения ее теста ( зеленый ), а затем пишете следующий (провальный) тест ( красный! ). И так далее…
Видите ли, вы на самом деле не стремитесь «завершить» ката, хотя с некоторыми более короткими вы наверняка это сделаете. Вы больше сосредоточены на следовании дисциплинированному процессу:
- Что следующая наша программа должна уметь делать?
- Какой тип теста мы можем написать, чтобы отразить следующее требование?
- Тест не пройден. Как мы можем изменить код, чтобы пройти тест? (требование выполнено)
- Тест проходит. Как мы можем реорганизовать код, чтобы сделать его более эффективным? (убедитесь, что тесты все еще проходят)
- Повторение
Конечно. Рад, что ты спросил.
Популярным (и коротким) является ката Fizz Buzz: (есть еще много на
Ссылка скрыта от гостей
)Это помогает перечислить требования, если ката это еще не делает:Напишите программу, которая печатает числа от 1 до 100. Но для кратных трех выведите «Fizz» вместо числа и для кратных пяти выведите «Buzz». Для чисел, кратных трем и пяти, выведите «FizzBuzz».
- Выведите числа от 1 до 100.
- Если число кратно 3, вместо этого выведите «Fizz».
- Если число кратно 5, вместо этого выведите «Buzz».
- Если число кратно 3 * и * 5, вместо этого выведите «FizzBuzz».
Шаг 1: вернуть тот же номер
Нам нужен метод, который принимает число и (пока) выплевывает его обратно. Давайте начнем с базового метода, который принимает число и выводит пустую строку, поэтому мы можем скомпилировать.
Код:
public class FizzBuzz
{
public string FizzyOutput(int input)
{
return "";
}
}
Давайте просто проверим 1 и 2, чтобы начать. Конечно, это не получается, потому что мы всегда возвращаем пустую строку:
Код:
[TestFixture]
public class FizzBuzzTests
{
private FizzBuzz fizzBuzz;
[SetUp]
public void Setup()
{
fizzBuzz = new FizzBuzz();
}
[Test]
public void FizzyOutput_OutputsOne_WhenInputIsOne()
{
var output = fizzBuzz.FizzyOutput(1);
Assert.AreEqual("1", output);
}
[Test]
public void FizzyOutput_OutputsTwo_WhenInputIsTwo()
{
var output = fizzBuzz.FizzyOutput(2);
Assert.AreEqual("2", output);
}
}
Теперь вы передадите клавиатуру своей паре, чтобы исправить код и выполнить тест.
Код:
public string FizzyOutput(int input)
{
return input.ToString();
}
Шаг 2: верните «Fizz» для кратных 3
Второе требование - напечатать «Fizz» для кратных 3. Ваш партнер может создать несколько тестов или с помощью инструмента, такого как NUnit, использовать атрибут TestCase. Запустите новый тест и посмотрите, как он провалится. В конце концов, мы все равно возвращаем одно и то же число, несмотря ни на что.
Код:
[TestCase(3)]
[TestCase(6)]
[TestCase(9)]
[Test]
public void FizzyOutput_OutputsFizz_WhenInputIsMultipleOfThree(int input)
{
var output = fizzBuzz.FizzyOutput(input);
Assert.AreEqual("Fizz", output);
}
Чтобы это произошло, вы можете сделать что-то глупое, например, вернуть «Fizz» точно для указанных входных данных. Или используйте более практичный подход, который обрабатывает любое число, кратное 3. Всегда выполняйте тесты снова, когда закончите, чтобы убедиться, что они пройдены.
Код:
public string FizzyOutput(int input)
{
if (input % 3 == 0)
return "Fizz";
return input.ToString();
}
Шаг 3: верните «Buzz» для кратных 5
Теперь вы пишете следующий тест, показывающий, что кратные 5 возвращают «Buzz», и снова проходите клавиатуру.
Код:
[TestCase(5)]
[TestCase(10)]
public void FizzyOutput_OutputsBuzz_WhenInputIsMultipleOfFive(int input)
{
var output = fizzBuzz.FizzyOutput(input);
Assert.AreEqual("Buzz", output);
}
Тест не пройден, и ваша пара исправляет его очень похоже на предыдущее требование.
Шаг 4: верните «FizzBuzz» для кратных 15
Последнее требование, и ваш партнер пишет тест для него .., кратный 3 * и * 5:
Код:
[TestCase(15)]
[TestCase(30)]
[TestCase(45)]
public void FizzyOutput_OutputsFizzBuzz_WhenInputIsMultipleOfThreeAndFive(int input)
{
var output = fizzBuzz.FizzyOutput(input);
Assert.AreEqual("FizzBuzz", output);
}
Ваш ход, чтобы закончить ката, и вы делаете это, проверяя на кратные 15:
Код:
public string FizzyOutput(int input)
{
if (input % 15 == 0)
return "FizzBuzz";
if (input % 3 == 0)
return "Fizz";
if (input % 5 == 0)
return "Buzz";
return input.ToString();
}
Запустите тесты еще раз и убедитесь, что все они пройдены. Вот полный набор тестов, которые мы проводим.
Хотя я думаю, что приведенных выше тестов достаточно, вы можете проверить каждое значение, просто чтобы быть уверенным:
Код:
[TestCase(1, "1")]
[TestCase(2, "2")]
[TestCase(3, "Fizz")]
[TestCase(4, "4")]
[TestCase(5, "Buzz")]
// ....
[TestCase(14, "14")]
[TestCase(15, "FizzBuzz")]
[TestCase(16, "16")]
// ...
[TestCase(95, "Buzz")]
[TestCase(96, "Fizz")]
[TestCase(97, "97")]
[TestCase(98, "98")]
[TestCase(99, "Fizz")]
[TestCase(100, "Buzz")]
public void FizzyOutput_OutputsExpectedValues(int input, string expectedOutput)
{
var actualOutput = fizzBuzz.FizzyOutput(input);
Assert.AreEqual(expectedOutput, actualOutput);
}
Ссылка скрыта от гостей
Последние мысли
На этом этапе мы могли бы снова провести рефакторинг, поскольку все тесты проходят успешно. Я не думаю, что есть еще много чего сделать. Ты видишь что-нибудь, что я пропустил? Опечатки?
Я использовал C #, потому что я наиболее знаком с ним, но вы могли бы соединиться с кем-то, кто знает другой язык. Я пару раз делал пару для создания каты в Ruby - никогда не повредит изучать что-то новое (и встречать кого-то нового!) И видеть, как другие программисты / языки подходят к тестированию.
Там нет давления бизнес-требований или сроков ... просто учиться у других и делиться знаниями. И, может быть, посмеяться, когда время истекло, и вы понимаете, что пришли к отвратительному решению (не то, что * такое * когда-либо случается).
Спасибо, что прочитали это далеко ... Я надеюсь, что вы узнали что-то новое или интересное.
Мысли? У вас есть вопросы или что-то добавить? Позвольте мне знать в комментариях ниже!
Источник:
Ссылка скрыта от гостей