Като част от QA академията на Телерик, наскоро ми се наложи да пиша unit тестове за едно конзолно приложение, а именно една игра. В близко бъдеще се надявам да ми остане малко повече време, за да ви представя урок как да си създадем лесно, бързо и приятно някаква old school готина игричка, но засега, да караме направо.
Ще ви споделя накратко как да подкарате компонентни тестове, които симулативно да обработват изкарване / вкарване на информация от конзолата.
Какво ще тестваме?
Няма да влизам в детайли относно същността и имплементацията на възложената задача. Ще дам конкретен пример за един метод от нея. В играта имаше изискване да има табло(таблица) с резултати или т. нар. scoreboard, който да пази имената на играчите и техния резултат. Също така, scoreboard-а трябва да изкарва играчите подредени във възходящ ред по резултата (в контекста на играта, това са брой опити за решаване на проблема, т. е. по-малкият брой опити е по-добър резултат). Ако двама играчи са с еднакъв резултат, то тогава да ги подрежда по имена (азбучен ред или още лексикално сортиране). Максималният брой играчи за показване е 5, а ако таблото е празно, се изкарва определено съобщение.
За целта използвах една много полезна структура от данни, а именно OrderedMultiDictionary. За нея, авторите й и повече информация относно другите многофункционални и интересни класове, които идват с безплатното assembly, посетете тази страница.
Ще пишем на C#. Работим с Microsoft Visual Studio 2010 Ultimate и
Team Test Framework-а към него.
Може да видите кода на моята имплементация – тук
или като картинка вляво.
Това е самият метод.
Компонентно тестване на нещо, което пише по конзолата
В същността на проблема стои въпроса как най-лесно да сравним отпечатаното на конзолата с това, което очакваме и сме посочили като expected value за нашия тест. За целта ще ни е нужно да пренасочим нанякъде стандартното печатане на конзолата. Това може да се направи лесно като използваме класа StringWriter дефиниран в namespace-а System.IO (за повече информация – MSDN).
Създаване на теста в стъпки
- Записваме някаква sample data в нашата структура. За целта използвам самостоятелно написан метод, който добавя в речника няколко играчи.
Може да се направи и чрез стандартно викане на метода Add на класа OrderedMultiDictionary, който ще иска да му бъде подаден като параметър обект от тип KeyValuePair<int, Competitor>. - Създаваме инстанция на класа StringWriter, например:
StringWriter sw = new StringWriter(); - Използваме статичния метод на конзолата Console.SetOut(TextWriter newOut) и като параметър подаваме създадената от нас инстанция на класа StringWriter, а именно sw.
- Викаме метода, който ще тестваме и който стандартно пише по конзолата.
В моя случай, това е споменатия вече по-горе статичен PrintScoreBoard(). Вече пренасочен, изходът ще се запише в обекта sw. - Създаваме си променлива от тип string, която да съдържа в себе си очаквания от нас резултат.
Важно е да се отбележи, че навсякъде, където очакваме нов ред, трябва да добавим към нашия низ литерал за нов ред. В примерчетата отдолу съм го направил използвайки placeholder и property-то NewLine на класа Environment, който универсално работи за всяка среда, под която се компилира приложението, добавяйки правилния литерал. - Използваме Assert.AreEqual, сравнявайки обекта sw обърнат до string със създадената от нас променлива.
- Пускаме теста и ако всичко е наред, трябва да е минал успешно.
Може да видите кода на моята имплементация – тук
или като картинка вляво.
Това са два теста на метода PrintScoreBoard().
Ами ако желаем да симулираме вкарване на информация от конзолата?
Това вече е малко по-абстрактно, но не толкова сложно. Използва се друг клас – StringReader (MSDN). Накратко казано, изходът отново се пренасочва към StringWriter, а входът към инстанция на StringReader.
Ще ви дам конкретен, лесен пример.
Това е написаният от нас метод и теста към него (двете картинки отгоре).
Питаме потребителя как се казва, четем отговора му и после отпечатваме поздрав.
Тестът работи по следния начин:
- Пренасочваме стандартния изход на конзолата към обект от тип StringWriter.
- Създаваме инстанция на класа StringReader и като параметър подаваме всичко, което очакваме от потребителя. В нашия случай, това е име последвано от нов ред (тъй като стандартно се вика Console.ReadLine()).
- Пренасочваме входа на конзолата към нашия обект от тип StringReader.
- Създваме си променлива от тип string, в която вписваме очаквания резултат. Важно е да се отбележи, че това ще е само изходната информация. Прочитането вече е извършено и ние очакваме резултат на базата на него.
Внимавайте също и с местата, на които се очаква да бъде отпечатан нов ред.
Често това води до грешки и пропадане на теста. - Сравняваме очакваното с обекта от тип StringWriter обърнат до string.
Ако всичко е наред, тестът минава успешно.
Писането на компонентни тестове е важно за осигуряване качеството на нашата разработка без значение дали тя е desktop, web или просто конзолно приложение. Надявам се този урок да е в помощ на онези от вас, които са се сблъскали със задачата да изтестват метод, който стандартно изкарва нещо на конзолата или пък чете от нея. Желая ви успех! : )
Супер обяснение! Симулацията на вкарване информация от конзолата беше това, което ми убягваше.