Титры к видео уроку - "Тестирование с использованием Mock объектов, Isolation Frameworks"

Ссылка на полный видео урок

Здравствуйте, мы начинаем третий урок по курсу TDD. Сегодня мы рассмотрим, что такое Mock-объекты и как пользоваться Isolation фреймворками.

Модульные тесты условно можно разделить на две группы: тесты состояния и тесты взаимодействия. Тетсы состояния базируются на использовании стаб объектов. Эти тесты проверяют, что вызываемый метод объекта работал корректно, проверяя при этом состояние объекта после вызова метода. Тесты взаимодействия базируются на mock-объектах . Этот тест проверяет объект на манипуляции с другими объектами.

Мок объектом мы будем называть управляемую замена существующих зависимостей в системе, которые заменяют реальные объекты системы и позволяют проверить вызовы своих членов тестируемым классом. Нужно чётко разделять мок объекты и стаб объекты. Между ними есть значительная разница: мок объекты могут быть причиной не прохождения теста, а стаб – не могут. Давайте перейдём на следующих тест и увидим разницу между использованием этих объектов.

На этом слайде слева мы видим состояние объекта, который использует стаб объект, справа тест взаимодействия, который использует мок объект. При использовании стаб объекта, тест направлен на проверку членов тестируемого класса, например: проверка возвращение значения метода. В таком случае тест будет проверят правильность работы логики метода. Стаб объект будет просто управляемой заменой неуправляемой зависимости в системе. Если мы рассмотрим тест, который использует мок объект, мы увидим, что тест направлен не на проверку членов тестируемого класса, а на проверку состояния мок объекта после взаимодействия с тестируемым классом. В первом случае тест может быть провален не подставным объектом, а непосредственно тестируемым классом. Во втором случае наоборот. Тут мы и видим основное различие межту стаб и мок объектами, между тестами состояние и взаимодействия. На следующем сладйде мы увидим пример использования мок объекта.

В этом примере мы дополним класс FileManager новым функционалом – методом Analise, который принимает имя некоего файла, проверяет, если у этох файла расширение не “.txt”, использую некий service этот Analize записывает в журнал ошибок сообщение “FileExtension error” то есть ошибка расширения файла. При этом FileManager использует внешнюю зависимость service, которую нужно заменить неким подставным подставным объектом. Это может быть либо мок объект, либо стаб объект. Мы понимаем, что для этого случая нужно использовать тест взаимодействия, который проверит, что метод Analise правильно вызывает метод LogError объекта service. Поэтому для замены мы, конечно же, будем использовать мок объект. Этот объект мы видим справа на нашем слайде – некий MockLogService, который содержит одно поле типа string lastError, а также метод LogError, который должен быть вызван методом Analizer объекта FileManager . Так при вызове LogError в поле LastError будет сохраняться тот параметр, который метод Analize будет передавать методу LogError в качестве параметра. При этом мы говорили, что те тесты, которые используют мок объекты должны быть направлены на проверку именно мок объекта.

В первом примере мы попробуем протестировать метод Analize объекта FileManager. Метод Analise принимает имя некоего файла, и проверяет, если длина файла меньше 8-и и формат файла не “.txt”, то вызывается метод LogError на объекте Service и записывается сообщение в журнал ошибок. При этом мы видим, что класс использует внешнюю зависимость - ILogService. Вндрение зависимости производится через конструктор. В данном примере мы будем использовать мок объект, для того чтобы проверить, что метод Analize действительно вызывает метод LogError на объекте Service. Для этого мы создали класс класс MockLogSErvice, который реализует интерфейс ILogService. У класса MockLogSErvice есть открытое поле типа string c именем LastError, а также есть метод LogError. Это реализация интерфейса ILogService. Этот метод принимает строковое значение – сообщение об ошибке и сохранять это сообщение в поле lasterror класса MockLogSErvice. Давайте перейдём к тесту и посмотрим, каким образом мы будем проверять, что объект FileManager взаимодействует с объектом MockLogSErvice. На 12 строке мы создаём экземпляр MockLogSErvice. 14 строка: создаём экземпляр FileManager и внедряем зависимость MockLogSErvice. 16 строка: вызываем метод Analize и передаём туда имя файла “SomeFile.log”. У нас должно быть записано две ошибки в LogService и последняя из них будет говорить от том, что разрешение не соответствует тем требованиям, которые установлены. 18 строка: вызываем метод AreEqual и проверим, что объект mockService имеет состояние “FileExtension error: SomeFile.log”. Это будет говорить о том, что объект FileManager и объект mockService провзаимодействывали. Давайте запустим наш тест. FileManagerTest1 у насуспешно завершился.

Во втором примере мы будем использовать одновременно мок и стаб объекты. Мы дополним метод Analize новым функционалом. Если метод Analize попытается послать объекту logService какое-то сообщение и при это logService не будет доступен, то сгенерируется исключительная ситуация, мы перейдём на 34 строку и пошлём сообщение в тех поддержку с сообщением исключительной ситуации с помощью объекта MailService. При этом объект logService у нас будет стаб объектом и объект MailService будет мок объектом. Таким образом мы будем проверять взаимодействие объекта FileManager с объектом MailService. Зависимость в класс FileManager мы будем внедрять с помощью конструктора. Теперь давайте посмотрим на наши подставные объекты: у нас есть объект StubLogService и MockMailService. Объект StubLogService реализует интерфейс ILogService, поэтому у него появляется метод LogError. Мы хотим смоделировать ситуацию,при которой объект logService у нас будет недоступен по той или иной причине. Поэтому мы реализовали метод LogError таким образом, что он будет генерировать исключительную ситуацию с текстом той ошибки, которую мы передаём этому методу. Объект MockMailService – объект, который будет проверять, что объект MailServicе послал какое-то сообщение в тех поддержку. В этом объекте у нас будет 2 поля типа stirng: massage и destination. Объект реализует интерфейcIMailService, поэтому у него появляется метод SendMail. Он просто напросто инициализирует поля massage и destination. Теперь давайте перейдём к тестовому и посмотрим как он реализован. Мы создали тестовый метод FileManagerTest2, на 12, 13 строках мы создали зависимости для объекта FileManager. 15 строка: мы создали экземпляр FileManager и внедрили в этот класс зависимость от объектов logService и mailServicе. 17 строка : мы подготовили имя файла, который мы хотим проанализировать. Далее на 19 строке мы вызвали метод Analize объекта Manger и передали туда имя файла “SomeFile.log”. В теле метода Analize вызов метода logSevice будет генерировать исключительную ситуацию с сообщением “Filename too short” и конкатенацией названия файла. Далее перейдём к блоку catch метода Analize и вызовем метод SendMail и пошлём туда сообщение. Дальше на 21строке в теле тестового метода мы вызовем метод AreEqual и проверим, что действительно объект FileManager проивзаимодействовал с объектом MailService. Давайте запустим тест. Он был успешно пройден.

При ручном создании стаб и мок объектов можно столкнуться со следующими проблемами: иногда сложно создать фейк объект для интерфейса, имеющего много членов; создание фейк объектов занимает много времени; приходится писать много кода для сохранения состояния часто вызываемого мок объекта; для проверки всех параметров вызываемых методов приходится делать множество проверок; проблема использования уже созданных мок и стоаб объектов в других тестах. И для того, чтобы избавиться от этих проблем, нужно использовать так называемые Isolation Frameworks. Isolation Frameworks – инструмент, который позволяет упростить процесс создания заглушек для классов, интерфейсов и методов, которые используются в тестируемом методе. Существует множество Isolation Frameworks: Rhino Mocks, Moq, Typemock Isolator, Microsoft Fakes, NMock2. Мы на этом курсе будем изучать Rhino Mocks – одна из популярных платформ для динамической генерации mock объектов, используемых в юнит тестировании приложений .Net Framework. Он позволяет создавать и использовать фейк объекты, используя две модели: первая из них называется Record and Play, вторая RangeActAssert. Мы с вами будем пользоваться перов. Теперь давайте рассмотрим как же работает эта модель.

Она предусматривает запись сценария, который должен быть выполнен при прохождении тестов посредством вызовов тестируемых методов с указанными входными и выходными данными. Если при прохождении теста заданный сценарий выполняется и соблюдаются входные/выходные данные, то тест можно считать успешно пройденным.

На следующем слайде продемонстрирован небольшой пример работы с Isolation фреймворком RinoMocks. Основным объектом, который позволяет ему использовать модель Recod and Play, является MockRepository. Он позволяет создавать сценарии, создавать мок объекты а также выполнять проверку работоспособности сценария. В данном примере сначала мы создали экземпляр класса MockRepository, дальше мі создали динамический мок обїект, которій реализует інтерфейс ILogService. Для этого мы вызываем метод DynamicMock на экземпляре класса MockRepository, в качестве параметра передаём объект того типа, который должен реализовывать наш интерфейс и возвращаем значение ILogService. Далее мы создаём сценарий. Сценария создаётся с помощью конструкции using, в качестве параметра которой нам нужно передать вызов метода Record на объект rhinoEngine. Сценарий будет указывать, что при выполнении теста на объекте logService должен вызваться метод LogError с параметром “TestErrorMessage”. Далее мы на объекте logService вызываем метод LogError с параметром “TestErrorMessage”. Осталось только проверить сценарий. Для этого существует метод Verify и VerifyAll на объекте rhinoEngine. Если мы используем первый, мы должны в качестве параметра указать тот объект, для которого мы создали сценарий и который мы хотим проверить. Тест будет успешно завершатся, так как на проверяемом объекте будет вызываться метод LogError с сообщением “TestErrorMessage”.

В этом примере мы с помощью MockRepository Isolation Framework RhinoMocks создадим динамический мок объект, который реализует интерфейс ILogService. В этом интерфейс у нас определён всего один метод – LogError. Этот метод принимает строковое значение и ничего не возвращает. После создания динамического мок объекта мы создаём сценарий, которому мы указываем, что на объекте, который мы создали, должен вызываться метод LogError с сообщением “TestErrorMessage”. На 24 строке мы вызываем этот метод с тем же сообщением на созданном нами объекте, и на 27 строке мы проверяем наш сценарий – вызываем Verify на объекте rhinoEngine и передаём туда созданный объект, который мы хотим проверить. Давайте запусти наш тест. Он успешно пройден. В следующем примере мы попробуем создать тест для объекта FileManager используя Isolation Framework RhinoMocks.

У нас есть объект FileManager, который определён методом Analize, который мы уже видели. Этот метод анализирует имя файла, и если у этого файла будет меньше 8 символов в названии, значит мы пошлём сообщение об ошибке “Filename too short”. Также, если расширение файла не “.txt” мы записываем ошибку в журнал. Дальше давайте перейдём в метод нашего теста и посмотрим, как мы будем тестировать наш сценарий и наш объект с помощью Isolation Framework-а RhinoMocks. 13 строка: мы создаём имя файла, который мы хотим проверять: “TestFilename.exe”. 15 строка: мы создаём объект MockRepository, 17 строка: мы создаём динамический мок объект, который реализует интерфейс ILogService. Дальше в блоке using мы указываем, что на объекте logService должен вызваться метод LogError с параметром “FileExtensionError” и конкатенацией с названием файла. 24 строка: мы создаём экземпляр FileManager, внедряем зависимость через конструктор с помощью объекта logService. На 25 строке вызываем метод Analize и передаём туда значение нашего файла. На 25 строке мы узнаем, что расширение файла не “.txt” и на 26 строке вызовем метод LogError на объекте logService, тем самым мы реализуем то сценарий, который был создан на 21 строке. На 27 строке с помощью метода Verify мы проверим правильность выполнения сценария. Тест наш успешно пройден.

В этом примере мы рассмотрим разные виды мок объектов, которые можно создать с помощью MockRepository. Два основные из них: Strict Mock и Dynamic Mock. С Dynamic Mock мы уже встречались. Разница использования этих двух объектов состоит в том, что для успешно выполнения теста, который использует Dynamic Mock объект, можно вызывать не только те методы, которые записаны в блоке using. Что касается Strict Mock объектов6 на них можно выполнять только те методы, которые указаны в сценарии. Если будут вызываться какие-то другие методы на этом объекте, то тест будет провален. Мы будем тестировать объекты, которые реализуют интерфейс ILogService. В этом интерфейсе находится два метод: LogError и OtherMetod. При этом первый метод будет вызван объектом FileManager при использования метода Analize для файлов, которые по длине название меньше 8-и символов, или же имеют расширение не “.txt”. В статической тестовом методе FileManagerStrictMockTest мы создали экземпляр StrictMock объекта, который реализует интерфейс ILogService, дальше создали сценарий, при котором должен вызываться метод LogError с некий сообщением. На 24 строке мы создали объект FileManager, внедрили в него зависимость с нашим мок объектом, на 25 строке вызвали метод Analize. На 27 строке проверяем, успешно выполнен ли наш сценарий. Запустим наш тест и посмотрим, что будет выведено в експлорере. Мы увидим, что тест у нас провален. Для того, чтобы увидить почему, нам придётся зайти в метод Analize, мы увидим, что кроме метода LogError на объекте service, также будет вызываться какой-то другой метод OtherMetod. И, так как мы используем мок объект, он не позволяет использовать методы, которые не указаны в сценарии, поэтому тест будет провален. Что касается использования Dynamic Mock, то можно вызывать какие угодно методы.

В этом примере мы узнаем, как созданный динамический мок объект Rhino Mocks можно заставить возвращать какие-то значения. На 15 строке мы создали экземпляр MockRepository, на 17 строке мы создали экземпляр динамического мок объекта, который реализует интерфейс ITestMock. В это интерфейсе находится всего один метод ReturnSomeString, который принимает значение int и возвращает стринговое значение. На 19 строке мы создаём блок using, чтобы записать сценарий использования нашего объекта. На 21 строке мы используем метод ReturnSomeString на объекте testMock и передаём туда значение “1”. С помощью объекта LactCall мы можем указать, какого типа значение должен возвращать метод ReturnSomeString нашего динамического мок объекта. При этом будет учитываться последий вызов. Мы указали, что последний вызов должен вернуть строку “1”. На 24 строке мы снова вызываем метод ReturnSomeString на объекте testMock с параметром “2”, и на 25 строке указали, что при таком вызове метода должна генерироваться исключительная ситуация типа ArgumentException.На 27 строке мы создали переменную типа int с именем input со значением “3”. На 28 строке мы вызываем метод ReturnSomeString со значением input, а на 29 строке мы указываем, что при таком вызове метода у нас должен выполняться следующий код: у нас должен создаться новый экземпляр TestDelegate, в котором должен быть выполнен метод, который принимает значение, и возвращает “string” cо конкатенированным значением, которое мы передали в качестве параметра нашему методу. На 32 строке мы вызываем метод ReturnSomeString со значением “1”. Если мы посмотрим на 22 строку, то вспомним, что при использовании в качестве параметра “1” должно возвращаться значение “string1”, что мы и проверим на 3 3строке. 35 строка: мы вызываем метод Throws на объекте Assert, тем самым проверяя, что при вызове метода ReturnSomeString с параметром “2” должна генерироваться исключительная ситуация ArgumentException. И 37 строка: при вызове метод ReturnSomeString с параметром 3 у нас должна возвращаться строка вида, указанного на 29 строке кода. На 38 строке проверим, равно ли возвращаемое значение значению “string3”. Давайте запустим наш тест и увидим, что всё у нас действительно отработало.

В этом уроке мы попробуем научится работать с системой ограничений, доступной для тестов по модели Record and Play. Для этого у объекта LastCall есть метод Constraints, который может принять экземпляр класса, который наследуется от базового класса для всех ограничений. Это класс AbstractContraints. В Rhino Mocks существует множество ограничений и эти ограничения доступны в пространстве имён Rhino.Mocks.Constraints и дальше множество вариантов. В данном примере мы с вами будет тестировать объект FileManager и его метод Analize, который может проверить имя файла, и если имя файла будет меньше 8-и символов или расширения файла – не “.txt”, то будет записываться ошибка в журнал с помощью объекта logService иуго метода LogError, и в случае генерация исключительной ситуации с помощью объекта mailService и его метода SendMail будет отправляться сообщение. Давайте перейдём к тестовому методу. На 14 строке мы подготовили имя файла – это “SomeFile.log”. На 16 строке с помощью объекта MockRepository мы создали стаб объект, который реализует интерфейс ILogService, и динамический мок объект интерфейса IMockSErvice. На 20 строке мы используем блок using для создания сценария выполнения нашего теста. На 22 строке мы вызываем метод LogError объекта logService с параметром “Whatever”. На 23 строке мы указываем, что при вызове метода должно работать ограничение Anything, которое говорит о том, что параметр метода не будет иметь никакого значения – можно передавать всё что угодно, при этом будет генерироваться исключительная ситуация с сообщением “TestMessage”. На 26 строке мы с вами указываем, что должен быть вызван метод SendMail на динамическом мок объекте mailService с параметрами Some@mail.mail и “TestMessage”. 29 строка: мы создаём экземпляр FileManager, создаём зависимость logSErvice, mailService. На 31 строке запускаем метод Analize для файла “SomeFile.log”. 33 строка мы проверяем сценарий, при этом, при вызове метода на объекте logService будет генерироваться исключительная ситуация и после этого будет вызван метод SendMail и будет отправлено сообщение “TestMessage” на адрес Some@mail.mail. Тест успешно пройден. Если мы закомментируем 23 строку, то тест не будет выполняться успешно, так как метод LogError будет выполняться с сообщением “Whatever”, а не с каким-то другим. В таком случае не будет работать наш тест.

В этом примере мы вернёмся к методу FindLogFile объекта FileManager и попробуем протестировать этот метод, используя Isolation Framework Rhino Mocks. Метод FindLogFile нам должен быть известен. Следует заметить, что у нас есть внешняя зависимость – это IDataAccessObject и зависимость внедряется с помощью конструктора. Давайте перейдём к тестовому методу. На 15 строке мы создаем экземпляр MockRepository, на 16 строке с помощью rhinoEngine мы создаём экземпляр стаб объекта, реализующий интерфейс IDataAccessObject. Для этого мы воспользовались методом Stab rhinoEngine. Нам возвращается некий экземпляр класса, реализующий интерфейс IDataAccessObject. На 18 строке мы создаём сценарий для работы с этим объектом. На 20 строке мы указываем, что должен быть вызван метод GetFiles объекта Stub. При этом мы указываем, что при последнем вызове должен возвращаться екземпляр закрытой коллекции List типа string со следующими элементами: “file1.txt”, “file2.log”, “file3.exe”. На 24 строке мы создаём экземпляр FileManager и внедряем зависимость через конструктор. На 26 строке вызываем метод IsTrue объекта Assert и проверяем, если FindLogFile вернёт нам значение “true”, то будем считать, что тест успешно пройден. Когда мы говорили о разнице между стаб и мок объектами, мы говорили, что основным отличием является то, что мок объект может провалить тест, а стаб – нет. Давайте его запустим. Тест успешно завершается, при этом метод VerifyAll позволяет проверить, выполнился ли тот сценарий, который мы указали в блоке using. Что касается стаб объектов – они не могут провалить тест с помощью даще с помощью Verify и VerifyAll. Закомментируем 28 строку. Сценарий нам нужен только для того, чтобы решать, какое значение будет возвращать наш стаб объект. К тому же, если даже закомментировать 26 строку и раскомментировать 28, то никакой ошибки не будет.

В этом и следующем примерах мы будем работать с ограничениями Contains, которые можно применять с помощью метода Constraints объекта LastCall. Этот параметр говорит о том, что параметр метода должен иметь строку, которую мы указываем в качестве конструктора объекта Contains. В нашем методе мы создали динамический мок объект, реализующий интерфейс IUnderTest. Этот метод имеет всего один метод – MethodToTest, который принимает строковое значение. В сценарии мы указали, что этот метод должен быть вызван с параметром “WhatEver”. Дальше мы создали ограничение, которое говорит о том, что MethodToTest должен вызваться с параметром “test”. 24 строка: мы вызываем наш метод с параметром “Expected test”. И действительно в этой строке есть слово “test”, и при вызове метода VerifyAll объекта MockRepository мы должны успешно пройти тест. Запускаем и тест успешно завершается. В следующем примере мы будет делать те же операции, но с объектом FileManager и уже знакомым нам его методом Analize.

На 24 строке теста мы создали имя файла, который хотим проанализировать. Этот файл называется “So.txt”. На 26 строке мы создали сценарий, который говорит о том, что метод LogError объекта logService должен иметь подстроку “Filename”. На 32 строке мы создаём экземпляр класса FileManager, на 26 вызываем метод Analize, и в качестве параметра передаём ему имя файла. В этом методе есть проверка, и так как название файла меньше восьми символов, будет запускать метод logError c параметром, имеющим в себе подстроку “Filename”, поэтому наш тест должен успешно завершиться. И следующим тест проверяет вызов метода LogError с помощью метода Analize объекта FileManager с параметром, который имеет подстроку “FileExtension”. Мы создали подобный сценарий, и при вызове метода Analize с проверкой, и, так как как расширение файла у нас не “.txt”, на 26 строке мы вызываем метод LogError с параметром, в котором содержится подстрока “FileExtension”. Этот тест должен быть успешно пройден.

В этом примере мы немного изменили интерфейс IMailService: теперь в нём есть метод содержит не два параметра, которые говорят о адресе получателе и тексте сообщения, а принимает значение экземпляра MailMessage, в котором есть конструктор, который принимает следующие параметры: адрес, на который будет отправлено сообщение, тема сообщения и сам текст сообщения. И для этого у MailMessage есть 3 свойства: Destination, Theme, Message Text соответственно. Теперь давайте посмотрим .как можно создать модульный тест для вот этого метода Analize. Давайте перейдём в объект FileManager и посмотрим те тесты, которые мы с вами создали. В первом тесте мы c помощью метода DynamicMock объекта MockRepository создаём мок объект IMailService. Дальше мы с вами создаём сценарий6 в этом сценарии должен быть вызван метод SendMail объекта mailService и в качестве параметра должен передаться экземпляр класса MailMassage c параметрами “testDestination”, “testTheme” и “testMessageText”. На 24 строке мы вызываем метод SendMail и передаём туда экземпляр класса MailMassage с теми же параметрами. На 26 строке мы проверяем, что сценарий выше действительно был реализован. Когда мы запустим этот тест мы увидим, что он у нас провалился. Почему? Потому как для сравнение программа будет использовать обыкновенный оператор сравнения на равенство. А так как экземпляр класса MailMassage не содержит обыкновенной логики, то сравниваться будут ссылки, и понятно, что у двух экземпляров классов, пусть и с одинаковыми значениями, ссылки будут разными. Поэтому для проведения теста мы будем использовать ограничение, которое есть в Rhino Mocks – Property. В этом примере мы создали экземпляр класса Dynamic Mock, который реализует интерфейс IMailService и ILogService. На 38 строке мы создали экземпляр FileManager, на 40 создали сценарий, по которому при вызове метода LogError с любым параметром, у нас будет генерироваться исключительная ситуация с сообщением “TestMessgame”. На46 строке при вызове метода MailMessage должны срабатывать ограничения. Для первого параметра должно сработать ограничение Property. Для создания этого ограничения мы будет пользоваться статическим методом Value, и указывать, что после вызова этого объекта, статический метод, который мы передаём в качестве параметра Destination должен иметь строку “TechSupport@mail.com”, свойство Theme с сообщением критической ситуации и MessageText должен иметь сам текст исключительной ситуации. На 52 строке мы вызываем метод Analize для файла “file.exe”, на 54 строке проверяем правильность выполнения сценария. Давайте выполним тест и видим .что тест успешно сработан.

Есть ещё один способ для проверки этой логики. Для этого мы создали ещё один тест – PropertyConstraintTestUsingAnd. В этом примере с помощью мы будем создавать экземпляр класса And, с помощью которого мы будет создавать набор ограничений, которые должны быть применены. Дальше с помощью ещё одного создания экземпляра класса And, мы говорим, что ограничение на свойство Destination и Theme нужно объединить со значением MessageText. Далее в блоке using мы будем вызывать метод Constraints с параметром combineAnd, который будет содержать ограничения для всех свойств. На 83 строке мы вызываем метод Analize с именем файла “file.exe” и на 85 строке мы проверяем на правильность выполнения нашего сценария. Видим, что этот тесть успешно проходит. В этом примере мы будем делать тоже самое6 проверять передан ли методом MailMasage объекта mailSеrvice его свойства в качестве параметров. И для этого мы будем пользовать ограничением Matching объекта Is. Оно позволяет создать экземпляр делегата предикат, которое будет содержать логику для проверки состояния того объекта, который мы пересылаем в качестве параметров SendMail. На 15 строке мы создали экземпляр исключительной ситуации, который мы хотим генерировать. На 19 и 20 строках мы создали зависимость IMailService и ILogService. Первый – динамический мок объект, второй – стаб объект. 21 строка: создаём экземпляр класса FileManager и внедряем зависимость logService и mailService. Дальше мы создаём сценарий, в котором при вызове метод LogError при любых параметрах будет генерировать исключительная ситуация. При вызове метода SendMail у нас должно запускаться создания экземпляра класса делегата предиката, с которым сообщен лямбда оператор, который будет принимать сообщение, которое передаёт SendMail и будет проверять, если у того объекта, который мы передаём в качестве значения метода в свойстве Destination строка “TechSupport@mail.com”, тема соответствует имени исключительной ситуации и сообщение соответствует сообщению исключительной ситуации, то наш предикат будет иметь значение “true”, что будет говорить о том, что наш метод вызван с теми параметрами, которые мы ожидаем. Если будет значение “false”, то при вызове метода VerifyAll будет генерироваться исключительная ситуация, и тест будет провален. Тест успешно проходит.

В этом и следующем примерах мы рассмотрим как с помощью Isolation Rhino Mocks можно тестировать приложение, которое использует модель событий. Для этого мы будем тестировать некое приложение, которое реализовано по паттерну ModelViewPresenter. У нас есть класс Presenter, который будет мостом между моделью и представлением. Также у нас есть интерфейсы IModule, который представляет модель, и IView, который определяет представление. В IView есть событие типа EventHandler с событием Load. В классе Presenter у нас есть два поля типа IModel и IView, есть также конструктор, который принимает экземпляры класса, представляющие интерфейсы IModel и IView. В теле конструктора инициализируются поля model и window класса Presenter и на 18 строке событию Load объекта window будет подписка обработчика View_Load, который у нас не реализован и генерирует исключительную ситуацию NotImplementException. В тесте для класса Presenter мы хотим убедиться, что экземпляр этого класса подписывает событие Load объекта window некий обработчик. В тестовом классе мы создали метод EventAttachedTest, который на 16 строке создает экземпляр динамического мок объекта IView. На 18 строке мы создали сценарий, в котором указали, что в любом случае у нас должна быть подписка любого обработчика на событие Load объекта view. На 24 строка мы создали экземпляр Presenter и на 26 строке проверили, что сценарий выполняется. Давайте запустимся и увидим, что событие было инициализировано. Давайте откроем следующий пример.

В следующем примере класс Presenter в принципе остался такой же, но тот обработчик, который мы подписываем на событие Load уже имеет какую-то логику. Этот обработчик будет выполнять метод DoSomeWork на model. Давайте перейдём в тестовый метод и посмотрим, каким методом мы будем тестировать данное приложение. На 16 строке мы создали стаб объект IView, на 17 – динамический мок объект IModel. На 19 строке мы создали сценарий, который говорит о том, что на объекте model должен выполниться метод DoSomeWork. На 24 строке мы создали экземпляр Presenter и внедрили в него зависимость view и model. На 26 строке с помощью объекта EventRaiser, который находится в библиотеке Rhino Mocks мы вызвали метод Create, который передаёт событие для того объекта, который передаётся у нас в качестве первого параметра и событии того имени, который передаётся в качестве второго параметра метода Create. В итоге работы этого метода мы получим значение типа IEventRaiser. На этом интерфейсе у нас доступен метод Raise, который позволяет вызвать события, которое мы указали на 26 строке. Мы вызовем событие, и, как мы знаем, событие имеет два параметра: первый – некий объект, который породил событие, второй – параметр, который наследуется от базового класса EventArgs. Мы вызвали события и на 29 строке и проверили, что на 21 строке на объекте model был вызван метод DoSomeWork. Давайте выполним наш тест и увидим, что он действительно прошёл.

© 2017 ITVDN, все права защищены