Как да използваме Moq за улесняване на модулното тестване в C #

Често трябва да пишем модулни тестове за код, който осъществява достъп до външен ресурс като база данни или файлова файлова система. Ако такива ресурси не са налични, единственият начин да се гарантира, че тестовете могат да се изпълнят е чрез създаване на фиктивни обекти. По същество, като се опирате на фалшиви реализации на тези основни зависимости, можете да тествате взаимодействието между тествания метод и неговите зависимости. Три от най-популярните подигравателни рамки за разработчиците на .Net са Rhino Mocks, Moq и NMock.

Сред тях Moq може да бъде най-гъвкавият и лесен за използване. Рамката Moq осигурява елегантен начин за настройка, тестване и проверка на макети. Тази статия представя дискусия на Moq и как може да се използва за изолиране на единици код от техните зависимости.

Първи стъпки с Moq

Можете да използвате Moq за създаване на фиктивни обекти, които симулират или имитират реален обект. Moq може да се използва за подигравка както на класове, така и на интерфейси. Има обаче няколко ограничения, които трябва да знаете. Класовете, които трябва да се подиграват, не могат да бъдат статични или запечатани, а подиграваният метод трябва да бъде маркиран като виртуален. (Обърнете внимание, че има решения за тези ограничения. Можете да се подигравате на статичен метод, като се възползвате от модела на дизайна на адаптера, например.)

Първата стъпка при използването на Moq е да го инсталирате, така че да можете да го използвате във вашия проект за единичен тест. Можете да изтеглите Moq от GitHub и да добавите референции според случая. Предпочитам обаче да инсталирам Moq чрез NuGet, защото е по-лесно и по-малко вероятно да пропуснете референции. Можете да инсталирате Moq, като използвате следната команда в командния ред на NuGet.

Install-Package Moq

Как да се подигравате с интерфейси, използвайки Moq

Нека започнем с подигравка с интерфейс. Синтаксисът за създаване на фиктивен обект с помощта на класа Mock е даден по-долу.

Mock mockObjectType = нов Mock ();

Сега помислете за следния интерфейс, наречен IAuthor.

публичен интерфейс IAuthor

    {

        int Id {get; комплект; }

        низ FirstName {get; комплект; }

        низ LastName {get; комплект; }

    }

Използвайки рамката Moq, можете да създадете фиктивен обект, да зададете стойности на свойствата, да посочите параметри и да върнете стойности при извикванията на метода. Следният кодов фрагмент илюстрира как можете да създадете екземпляр от интерфейса на IAuthor, използвайки Moq.

var mock = нов Mock ();

Обърнете внимание, че класът Mock принадлежи към рамката Moq и съдържа родов конструктор, който приема типа интерфейс, който искате да създадете. Moq се възползва от ламбда изразите, делегатите и генеричните продукти. Всичко това прави използването на рамката много интуитивно.

Следният кодов фрагмент показва как можете да се подигравате с интерфейса IAuthor и да предоставите на свойствата на подигравания екземпляр със съответните стойности. Обърнете внимание как използваме Assert, за да проверим стойностите на свойствата на подигравания екземпляр.

var автор = нов Mock ();

author.SetupGet (p => p.Id). Връща (1);

author.SetupGet (p => p.FirstName) .Returns (“Joydip”);

author.SetupGet (p => p.LastName) .Returns (“Kanjilal”);

Assert.AreEqual (“Joydip”, автор.Object.FirstName);

Assert.AreEqual (“Kanjilal”, автор.Object.LastName);

Как да се подигравате с методи, използвайки Moq

Нека сега разгледаме следния клас, наречен Article. Класът Article съдържа само един метод, наречен GetPublicationDate, който приема ID на статия като параметър и връща датата на публикуване на статията.

член от публичния клас

    {

        публичен виртуален DateTime GetPublicationDate (int articleId)

        {

            хвърли нов NotImplementedException ();

        }

    }

Тъй като методът GetPublicationDate все още не е реализиран в класа Article, методът е подиграван, за да върне текущата дата като дата на публикуване, както е показано в кодовия фрагмент, даден по-долу.

var mockObj = нов Mock ();
mockObj.Setup (x => x.GetPublicationDate (It.IsAny ())). Връща ((int x) => DateTime.Now);

Методът за настройка се използва за определяне на поведението на метод, който му се предава като параметър. В този пример се използва за определяне на поведението на метода GetPublicationDate. Извикването към It.IsAny()предполага, че методът GetPublicationDate ще приеме параметър от тип integer; Itсе отнася до статичен клас. Методът за връщане се използва за задаване на връщаната стойност на метода, посочена в извикването на метода за настройка. В този пример методът за връщане се използва за задаване на връщаната стойност на метода като текуща системна дата.

Moq ви позволява да проверите дали е бил извикан определен метод или свойство. Следният кодов фрагмент илюстрира това.

mockObj.Verify (t => t.GetPublicationDate (It.IsAny ()));

Тук използваме метода Verify, за да определим дали GetPublicationDate е бил извикан за фалшивия обект.

Как да се подигравате с методите на базовия клас, използвайки Moq

Обмислете следната част от кода. Тук имаме два класа - клас RepositoryBase и клас AuthorRepository, който го разширява.

публичен абстрактен клас RepositoryBase

{

    публичен виртуален bool IsServiceConnectionValid ()

    {

        // Някакъв код

    }

}

публичен клас AuthorRepository: RepositoryBase

{

    публична невалидност Запазване ()

    {

        if (IsServiceConnectionValid ())

        {

            // Някакъв код

        }

    }

}

Сега да предположим, че искаме да проверим дали връзката с базата данни е валидна. Възможно е обаче да не искаме да тестваме целия код вътре в метода IsServiceConnectionValid. Например методът IsServiceConnectionValid може да съдържа код, който се отнася до библиотека на трета страна. Не бихме искали да тестваме това, нали? Ето къде методът CallBase в Moq идва на помощ. 

В ситуации като тази, когато имате метод в базовия клас, който е заменен в подигравания тип и трябва да се подигравате само с основната версия на заменения метод, можете да рисувате на CallBase. Следният кодов фрагмент показва как можете да създадете частичен фалшив обект на класа AuthorRepository, като зададете свойството CallBase на true.

var mockObj = нов Mock () {CallBase = true};

mockObj.Setup (x => x.IsServiceConnectionValid ()). Връща (вярно);

Рамката Moq улеснява създаването на фалшиви обекти, които имитират поведението на класове и интерфейси за тестване, само с необходимата функционалност. За повече информация относно тестването с макети вижте тази страхотна статия от Мартин Фаулър.