Разработчики программного обеспечения, демографически, любят обсуждать разработку программного обеспечения. Некоторые из этих дебатов бушуют бесконечно на протяжении многих лет, в то время как другие стремятся к как минимум некоторому консенсусу. Модульное тестирование, похоже, попадает в эту последнюю категорию.
Посмотрите на этот вопрос переполнения стека, который спрашивает, стоит ли модульное тестирование усилий. Четыре самых популярных ответа составляют 1348 голосов, и все они предлагают некоторую форму квалифицированного или неквалифицированного ответа «да». Только пятый по популярности ответ, с микроскопическими 38 голосами, предлагает «нет». И это предлагает квалифицированное нет в этом. Принимая во внимание переполнение стека в качестве барометра, кажется, что у нас есть хоть какой-то консенсус по этому вопросу.
Это, безусловно, многообещающе для практики. Но это волшебным образом не делает всех желающими или способными сделать это. В конце концов, в мире существует общее мнение, что бег и нить являются хорошими идеями, но люди все равно предпочитают не делать этого.
Как и в случае с бегом и нитью, время представляет собой барьер для усыновления. Вы должны найти время в своем дне, чтобы начать делать что-то новое. Но в отличие от бега и нити, модульное тестирование имеет нетривиальную кривую обучения. И, хотя я не могу легко помочь вам найти больше времени в вашем дне, я могу помочь вам с кривой обучения.
Итак, давайте сегодня посмотрим на модульное тестирование в C #. Я пишу этот пост, предполагая, что у вас нет предварительных знаний о практике и вы вооружены только интересом узнать, как она работает.
Модульное тестирование в C #: ценностное предложение
Любой, кто собирается делать что-то новое, должен разумно спросить: «Что для меня?» Другими словами, прежде чем делать это, вы должны понять его ценностное предложение для вас. Почему юнит тест?
Ну, это добавляет товарный навык к вашему резюме. Но давайте посмотрим дальше. Блоггер, пишущий целый пост на эту тему, говорил о доверии как о наиболее важном преимуществе. Я склонен согласиться с этим. Модульное тестирование дает вам уверенность в том, что ваш код делает то, что вы думаете. Это также позволяет вам вносить изменения в кодовые базы с большей уверенностью, чем вы могли бы.
Как оно работает? Что ж, давайте рассмотрим, как вы подходите к тестированию ваших изменений сегодня. Вы вносите некоторые изменения в код, а затем запускаете приложение, чтобы убедиться, что ваши изменения имели ожидаемый эффект. Вы старательно проверяете, что новое поведение выглядит правильно, и вы также убедитесь, что ничего не сломали. Тогда вы делаете это и называете это днем.
У вас достаточно высокая уверенность в том, что ваши новые изменения верны. И у вас есть уверенность, что они не создавали проблем в других местах. По крайней мере, у вас есть такая уверенность сегодня. Через неделю, месяц или год кто знает? Это будет зависеть от того, будете ли вы или другие члены команды возиться с тем, что вы только что сделали.
Но что, если я скажу вам, что вы могли бы сделать «достаточно высокую» уверенность в своих изменениях и «некоторую» уверенность в отсутствии регрессий намного, намного выше? А что, если я скажу вам, что вы всегда сможете сохранить этот высокий уровень уверенности?
Это ценностное предложение модульного тестирования.
Модульное тестирование в C #: самый простой маленький пример
Хорошо, давайте предположим, что мой шаг продал вас. По крайней мере, это продало тебя достаточно, чтобы продолжать читать. На этом этапе большинство людей, обучающих вас, начинают с формального определения модульного тестирования. Я собираюсь отказаться от этого подхода, потому что я хочу показать вам, насколько концептуально легко это может быть. Забудьте, на данный момент, о юнит-тестах, тестовых проектах и всем том, что заставляет вас говорить «возможно, завтра». Вот модульное тестирование на простом старом C #, которое вы знаете и любите.
Допустим, вы написали следующую реализацию убийцы.
public class Calculator
{
public int Add(int x, int y)
{
return x + y;
}
}
Что бы вы сделали, чтобы проверить это? Предполагая, что эта вещь использовала какой-то графический интерфейс, вы можете открыть этот графический интерфейс, ввести два числа и проверить результат. «Давайте посмотрим, я поставил 15 и 20 и вернулся 35 … кажется правильным».
Но это требует много ручных усилий. И это также не длится за пределами вашего ручного тестирования. Что делать, если вы написали программу с автоматической проверкой? Что делать, если вы сделали что-то подобное?
static void Main(string[] args)
{
var calculator = new Calculator();
if (calculator.Add(15, 20) != 35)
throw new InvalidOperationException();
}
Вы могли бы достичь этого довольно просто. В том же решении, что и ваш класс Calculator, вы создаете новый консольный проект со ссылкой на ваш код. Напишите этот код в Main, и теперь вы можете запускать его в любое время. Если он выходит без проблем, все выглядит хорошо. Если вы запустите это и получите исключение, вы знаете, что кто-то испортил метод Add () и каким-то образом сломал его.
Модульное тестирование в C # правильный путь
Да, автоматизированный блок тестирования действительно является это концептуально проста. И это показывает вам происхождение практики. Разработчики поняли, что могут автоматизировать проверку своего кода. Вскоре после этого, будучи разработчиками, они начали создавать всевозможные фреймворки для этого. Давайте теперь посмотрим, как и почему они это сделали, потому что это поможет вам понять, как работают модульные тесты при стандартной реализации.
Подумайте о главном коде выше. Что происходит, когда вы хотите добавить больше тестов? Вы продолжаете втискивать все больше и больше кода в этот метод, и поэтому он дезорганизован. Возможно, вы извлекаете чеки в свои собственные методы, но у вас все еще есть проблема с пониманием того, что не удалось и почему, когда вы его запускаете. И как вы собираетесь делать все то, что делают люди, например, интегрировать это с вашей сборкой?
Вот где приходит установленный инструментарий. Он беспокоится об этом, так что вам не нужно. Это действительно все, что отличает стандартную технологию модульного тестирования от простой концепции, которую я показал вам выше. Вот что дают вам фреймворки и тестовые бегуны.
Реализация простого модульного теста
Чтобы все было как можно проще, давайте полностью останемся в Visual Studio. У вас есть класс Калькулятор, но вы хотите проверить его. И вы хотите протестировать его правильно, не добавляя собственную схему запуска тестового теста.
Вы можете следовать этому подробному объяснению, чтобы сделать это. Но если вам нужна короткая версия, вы создадите новый проект Unit Test в своем решении, как показано здесь.
Условно говоря, вы можете назвать его Production.Tests, где Production представляет название проекта, который вы тестируете. Обычно вам нужен проект модульного тестирования для каждого проекта производственного кода.
Как только вы это сделаете, у вас будет новый проект в вашем редакторе с одним файлом, содержащим следующий код.
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
Давайте немного изменим это, и ради хорошего именования, и ради чего-то полезного. Но, прежде чем сделать это, обязательно добавьте ссылку в этом тестовом проекте в ваш производственный проект, чтобы вы могли создать экземпляр класса Calculator. Посмотрите на новый код.
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Adding_10_And_15_Returns_25()
{
var calculator = new Calculator();
int result = calculator.Add(10, 15);
Assert.AreEqual<int>(25, result);
}
}
Вы заметите, что вы больше не выбрасываете исключения, указывающие на ошибку. Вместо этого вы вызываете этот метод Assert и делаете это в обозначениях [TestClass] и [TestMethod]. Чтобы все это работало, просто зайдите в меню и выполните Test-Run-> All (сочетание клавиш «Ctrl + R, A»). Это приведет к появлению окна обозревателя тестов, в котором будет показан тестовый запуск, который будет включать в себя один проходной тест. Если вы измените первый параметр на Assert.AreEqual (), вы увидите, что он не работает.
Что тут происходит?
Итак, вы успешно перешли от чего-то, что вы полностью понимаете, к чему-то более волшебному. Давайте теперь распакуем это немного, чтобы демистифицировать.
Когда вы создаете проект модульного теста, Visual Studio по своей сути понимает некоторые вещи об этом. Атрибуты TestClass и TestMethod предоставляют ему контекстные подсказки по соглашению. Эти вещи говорят: «Эй, тестовый бегун, обратите внимание на этот класс и метод!»
Тестовый прогон — это особый способ выполнения проекта, который реализует Visual Studio. Он говорит: «Я выполню этот проект, отыскивая все, что помечено как TestMethod, и выполняя код в нем. Я буду интерпретировать исключения и неудачные утверждения как ошибки, а все остальное — как проходы». И, действительно, в основе всего, это все.
Посмотрев на него через этот объектив, вы поймете, как на самом деле это просто более сложная версия нашей более ранней реализации в Main. Но теперь вы получаете ряд соглашений, которые понимает сообщество разработчиков, и вы можете интегрировать их со всеми видами инструментов для сборки и другими.
Это только начало
Надеюсь, теперь у вас есть понимание модульного тестирования в C # из первых принципов. Основная предпосылка действительно проста и мощна. Не позволяйте всем технологиям и опыту, наложенным на него, отвлекать вас от этого.
Но в то же время следует понимать, что мир узнал много нового о модульном тестировании за десятилетия, прошедшие с момента его внедрения и автоматизации. Теперь, когда вы понимаете основы, у вас есть много знаний и практики, которые нужно сделать. И нет лучшего времени для начала, чем сейчас.