Как лучше всего протестировать метод, который ничего не возвращает? В частности, в C#.
На самом деле я пытаюсь протестировать метод, который берет файл журнала и анализирует его на предмет определенных строк. Затем строки вставляются в базу данных. Ничего такого, что не было сделано раньше, но это ОЧЕНЬ новинка для TDD. Мне интересно, можно ли это протестировать или это что-то, что на самом деле не проверяется.





это будет иметь какое-то влияние на объект .... запросить результат эффекта. Если это не дает видимого эффекта, то модульное тестирование не стоит!
Зависит от того, что он делает. Если у него есть параметры, передайте имитации, которые вы могли бы позже спросить, были ли они вызваны с правильным набором параметров.
Согласовано - проверка поведения макетов тестирования метода была бы одним из способов.
Как всегда: проверьте, что должен делать этот метод!
Должен ли он где-то изменить глобальное состояние (э-э, запах кода!)?
Должен ли он вызывать интерфейс?
Должен ли он вызывать исключение при вызове с неправильными параметрами?
Должен ли он не вызывать исключения при вызове с правильными параметрами?
Должен ли он ...?
Предположительно, метод что-то делает, а не просто возвращает?
Если это так, тогда:
Если вы сообщите нам, что делает этот метод, я мог бы быть более конкретным.
Если метод ничего не возвращает, это одно из следующих
Императивные методы - вы можете проверить, действительно ли задача была выполнена. Проверьте, действительно ли произошло изменение состояния. например
void DeductFromBalance( dAmount )
можно проверить, проверив, действительно ли сообщение о балансе меньше начального значения на dAmount
Информационные методы - редко используются в качестве члена открытого интерфейса объекта ... следовательно, обычно не проходят модульное тестирование. Однако при необходимости вы можете проверить, выполняется ли обработка уведомления. например
void OnAccountDebit( dAmount ) // emails account holder with info
можно проверить, проверив, отправляется ли электронное письмо
Опубликуйте более подробную информацию о вашем реальном методе, и люди смогут лучше ответить.
Обновлять: Ваш метод выполняет две задачи. Я бы фактически разделил его на два метода, которые теперь можно протестировать независимо.
string[] ExamineLogFileForX( string sFileName );
void InsertStringsIntoDatabase( string[] );
String [] можно легко проверить, предоставив первому методу фиктивный файл и ожидаемые строки. Второй вариант немного сложен ... вы можете либо использовать Mock (google или search stackoverflow на имитирующих фреймворках), чтобы имитировать БД, либо нажать на фактическую БД и проверить, были ли строки вставлены в правильное место. Поищите в эта ветка хорошие книги ... Я бы порекомендовал Pragmatic Unit Testing, если вы испытываете затруднения. В коде это будет использоваться как
InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );
эй, гишу, хороший ответ. Пример, который вы привели ... разве это не тесты интеграции ...? и если да, то остается вопрос, как на самом деле тестировать методы Void ... может быть, это невозможно?
@andy - зависит от вашего определения «интеграционных тестов». Императивные методы обычно изменяют состояние, поэтому вас можно проверить с помощью модульного теста, который опрашивает состояние объекта. Информационные методы могут быть проверены с помощью модульного теста, который включает фиктивного слушателя / соавтора, чтобы гарантировать, что испытуемый выдает правильное уведомление. Я думаю, что оба могут быть разумно протестированы с помощью модульных тестов.
@andy база данных может быть смоделирована / отделена интерфейсом аксессора, что позволяет протестировать действие с помощью данных, переданных в фиктивный объект.
Сохранение в базе данных, похоже, не попадает ни в одну из двух категорий, если только оно не считается «уведомлением кого-то», что является странной характеристикой. Возможно, вы захотите уточнить там свои формулировки.
@MarredCheese - разве это не подпадает под императив? Вы можете проверить это, выполнив загрузку, чтобы убедиться, что строки действительно сохраняются, то есть изменение состояния.
Проверьте его побочные эффекты. Это включает:
Конечно, есть предел того, как вы можете тестировать много. Например, вы обычно не можете тестировать со всеми возможными входами. Тестируйте прагматично - достаточно, чтобы дать вам уверенность в том, что ваш код спроектирован надлежащим образом и правильно реализован, и достаточно, чтобы действовать в качестве дополнительной документации для того, чего может ожидать вызывающий.
Используйте Носорог издевается, чтобы установить, какие вызовы, действия и исключения могут ожидаться. Предполагая, что вы можете имитировать или вырезать части своего метода. Трудно узнать, не зная некоторых особенностей метода или даже контекста.
Ответом на вопрос, как проводить модульное тестирование, никогда не должно быть стороннего инструмента, который сделает это за вас. Тем не менее, когда человек знает, как проводить модульное тестирование, использование стороннего инструмента для облегчения его работы является приемлемым.
Типы возврата / подпрограммы - старые новости. Я не создавал возвращаемый тип Void (если только я не был очень ленив) примерно за 8 лет (со времени этого ответа, так что незадолго до того, как был задан этот вопрос).
Вместо такого метода, как:
public void SendEmailToCustomer()
Создайте метод, соответствующий парадигме Microsoft int.TryParse ():
public bool TrySendEmailToCustomer()
Возможно, нет никакой информации, которую ваш метод должен вернуть для использования в долгосрочной перспективе, но возврат состояния метода после того, как он выполнит свою работу, очень полезен для вызывающей стороны.
Кроме того, bool - не единственный тип состояния. Есть несколько случаев, когда ранее созданная подпрограмма могла фактически возвращать три или более разных состояния (хорошее, нормальное, плохое и т. д.). В таких случаях вы просто использовали бы
public StateEnum TrySendEmailToCustomer()
Однако, хотя Try-Paradigm отчасти отвечает на этот вопрос о том, как проверить возврат недействительности, есть и другие соображения. Например, во время / после цикла «TDD» вы выполняете «рефакторинг» и замечаете, что делаете две вещи с вашим методом ... тем самым нарушая «принцип единой ответственности». Так что об этом нужно позаботиться в первую очередь. Во-вторых, вы могли определить зависимость ... вы касаетесь «постоянных» данных.
Если вы выполняете доступ к данным в рассматриваемом методе, вам необходимо выполнить рефакторинг в n-уровневую или n-уровневую архитектуру. Но мы можем предположить, что когда вы говорите «Строки затем вставляются в базу данных», вы на самом деле имеете в виду, что вызываете уровень бизнес-логики или что-то в этом роде. Да, предположим.
Когда ваш объект создан, вы теперь понимаете, что у вашего объекта есть зависимости. Это когда вам нужно решить, собираетесь ли вы выполнять внедрение зависимостей на объекте или на методе. Это означает, что вашему конструктору или рассматриваемому методу нужен новый параметр:
public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert)
Теперь, когда вы можете принять интерфейс вашего объекта уровня бизнеса / данных, вы можете смоделировать его во время модульных тестов и не иметь никаких зависимостей или опасений «случайного» интеграционного тестирования.
Итак, в вашем живом коде вы передаете НАСТОЯЩИЙ объект IBusinessDataEtc. Но в модульном тестировании вы передаете объект MOCK IBusinessDataEtc. В этот макет вы можете включить свойства, не относящиеся к интерфейсу, такие как int XMethodWasCalledCount или что-то, чье состояние (я) обновляется при вызове методов интерфейса.
Таким образом, ваш модульный тест будет проходить через ваш Method (s) -In-Question, выполнять любую имеющуюся у них логику и вызывать один или два или выбранный набор методов в вашем объекте IBusinessDataEtc. Когда вы делаете свои утверждения в конце модульного теста, у вас есть пара вещей, которые нужно проверить сейчас.
IBusinessDataEtc.Для получения дополнительной информации об идеях внедрения зависимостей на уровне построения ... поскольку они относятся к модульному тестированию ... загляните в шаблоны проектирования Builder. Он добавляет еще один интерфейс и класс для каждого текущего интерфейса / класса, который у вас есть, но они очень крошечные и обеспечивают ОГРОМНОЕ увеличение функциональности для лучшего модульного тестирования.
Первый кусок этого замечательного ответа служит замечательным общим советом для всех начинающих / программистов среднего уровня.
Разве это не должно быть что-то вроде public void sendEmailToCustomer() throws UndeliveredMailException?
@ A.Emad Хороший вопрос, но нет. Управление потоком кода с помощью генерации исключений - давно известная плохая практика. Хотя в методах void, особенно в объектно-ориентированных языках, это был единственный реальный вариант. Самая старая альтернатива Microsoft - это обсуждаемая мною Try-Paradigm и парадигмы функционального стиля, такие как Monads / Maybes. Таким образом, команды (в CQS) могут по-прежнему возвращать ценную информацию о статусе вместо того, чтобы полагаться на бросок, что похоже на GOTO (что, как мы знаем, плохо). throwing (и goto) медленные, трудные для отладки и не очень хорошая практика.
Спасибо, что прояснили это. Это специфично для C# или вообще плохая практика - выбрасывать исключения даже на таких языках, как Java и C++?
Попробуй это:
[TestMethod]
public void TestSomething()
{
try
{
YourMethodCall();
Assert.IsTrue(true);
}
catch {
Assert.IsTrue(false);
}
}
Какой бы экземпляр вы ни использовали для вызова метода void, вы можете просто использовать Verfiy
Например:
В моем случае его _Log - это экземпляр, а LogMessage - метод, который нужно протестировать:
try
{
this._log.Verify(x => x.LogMessage(Logger.WillisLogLevel.Info, Logger.WillisLogger.Usage, "Created the Student with name as"), "Failure");
}
Catch
{
Assert.IsFalse(ex is Moq.MockException);
}
Выдает ли Verify исключение из-за сбоя метода, который тест не прошел?
Вы даже можете попробовать это так:
[TestMethod]
public void ReadFiles()
{
try
{
Read();
return; // indicates success
}
catch (Exception ex)
{
Assert.Fail(ex.Message);
}
}
Полагаю, это самый простой способ.
Пожалуйста, не используйте термин «TDD», если это не то, что вы делаете. Вы выполняете модульное тестирование, а не TDD. Если бы вы выполняли TDD, у вас никогда не возникло бы вопроса «как протестировать метод». Сначала будет существовать тест, а затем возникнет вопрос: «Как пройти этот тест?» Но если бы вы БЫЛИ выполняли TDD, ваш код был бы написан для теста (а не наоборот), и вы, по сути, ответили бы на свой собственный вопрос. Ваш код будет отформатирован по-другому в результате TDD, и эта проблема никогда не возникнет. Просто уточняю.