ASP.NET: EntityFaker helper for unit-testing или помощник для написания Unit-тестов
Сайтостроение | создано: 05.08.2014 | опубликовано: 06.08.2014 | обновлено: 13.01.2024 | просмотров: 5452
Писать тесты при написании программ (сайтов, библиотек, контролов) признак хорошего тона в программировании. А если учесть, что написание тестов требуют достаточно много времени, то помощник в этом деле не помешает.
Что это такое EntityFaker
Это библиотека создана для упрощения написания тестов для ASP.NET MVC, но я уверен, что вы можете использовать ее и в других типах проектов. Генератор призван создавать сущности и заполнять примитивные типы свойств (только открытые) простыми данными.
Инструменты и всё такое
Я буду использовать Visual Studio 2013 Ultimate, проект на платформе ASP.NET MVC 5. В отдельном проекте отведенном под тесты у меня для генерации заглушек используется RhinoMocks. Для написания тестов я использую на этот раз MSTests.
Как установить
Для того чтобы в вашем тестовом проекте появилась возможность использовать генератор, надо установить nuget-пакет:
PM> Install-Package EntityFaker Installing 'EntityFaker 1.0.1'. Successfully installed 'EntityFaker 1.0.1'. Adding 'EntityFaker 1.0.1' to Calabonga.Mvc.Lipix. Successfully added 'EntityFaker 1.0.1' to Calabonga.Mvc.Lipix. PM>
Это пример установки в мой проект.
Пример использования
Допустим у меня есть некий класс, который мне нужно использовать в тестах.
public class Picture : IdentityBase { [Required] [StringLength(50)] [Display(ResourceType = typeof(Resources.Resources), Name = "Route")] public string Name { get; set; } [Display(ResourceType = typeof(Resources.Resources), Name = "DisplayName")] [StringLength(50)] public string DisplayName { get; set; } [Display(ResourceType = typeof(Resources.Resources), Name = "Description")] [DataType(DataType.MultilineText)] public string Description { get; set; } [Required] [Display(ResourceType = typeof(Resources.Resources), Name = "CreatedAt")] [DataType(DataType.DateTime)] public DateTime CreatedAt { get; set; } [Display(ResourceType = typeof(Resources.Resources), Name = "LibraryId")] public int LibraryId { get; set; } [ForeignKey("LibraryId")] public virtual Library Library { get; set; } [Display(ResourceType = typeof(Resources.Resources), Name = "DisplayNameEn")] [StringLength(50)] public string DisplayNameEn { get; set; } [Display(ResourceType = typeof(Resources.Resources), Name = "DescriptionEn")] [DataType(DataType.MultilineText)] public string DescriptionEn { get; set; } public string Src { get { var library = string.Empty; if (Library != null) { library = Library.Name; } return string.Format("/gallery/{0}/{1}", library, Name); } } }
Также у меня есть некоторый слой логики, который управляется этими классами. Я покажу интерфейс:
public interface IImagesProcessor { PagedList<Category> GetCategories(int pageIndex, int defaultPageSize); PagedList<Library> GetLibraries(int pageIndex, int defaultPageSize, Category category); PagedList<Picture> GetPictures(int pageIndex, int defaultPageSize, string library); Picture GetById(int id); void Delete(Picture item); void Update(Picture item); }
Unit-тесты в примерах
Итак, для того чтобы было проще описать как использовать этот помощник, сразу приведу пример теста. Для начала метод инициализации теста:
[TestInitialize] public void Setup() { var context = MockRepository.GenerateStub<IContext>(); context.Categories = DataContextBuilder.GetCategoriesDbSet(); context.Pictures = DataContextBuilder.GetPisturesDbSet(_picturesCount); var logNotify = MockRepository.GenerateStub<ILogNotifyService>(); var fileService = MockRepository.GenerateStub<IFileService>(); _processor = new ImagesProcessor(context, fileService, logNotify); }
Немного прокомментирую приведенный листинг. Строка 3 – создается заглушка для IContext, в моем проекте этот интерфейс представляет DbContext. Вот этот интерфейс для ознакомления:
public interface IContext { IDbSet<Category> Categories { get; set; } IDbSet<Library> Libraries { get; set; } IDbSet<Picture> Pictures { get; set; } // много удалено для краткости ... }
Но не в этом “соль”. Обратите внимание на строки 4-5, где как раз в сгенерированную “заглушку” дополняются данными некоторые свойства, а именно Categories и Pictures. Вот как выглядит билдер:
public static class DataContextBuilder { public static IDbSet<Category> GetCategoriesDbSet(int count = 5) { return DbSetter<Category>.GetObject(EntityFaker.CreateListOf<Category>(count)); } public static IDbSet<Picture> GetPisturesDbSet(int count = 5) { return DbSetter<Picture>.GetObject(EntityFaker.CreateListOf<Picture>(count)); } }
Вот самая “соль”. В строке 5 и 10 используется EntityFaker для генерации коллекций сущностей, однако, можно генерировать и единичные сущности. Чтобы было совсем всё понятно, если я захочу создать один объект типа Category, я вызову генератор так:
Как вы видите, ничего хитрого нет, свойства заполняются значением названий свойств с подстановкой номера по порядку по индексу генерации. А если я вызову метод (как в строке 5 предыдущего листинга), то результатом будет:
Для построения простых тестов такое количество данных и такое качество данных полностью удовлетворяет потребностям. Для более сложных тестов (хотя в соответствии с одной и парадигмой Unit-тестирования, тесты должны быть просты, но множественны) придется в созданном классе (-ах) подменить некоторые данные или искать другие варианты генерации. И конечно же, никто не исключает использование Mock-фреймворков.
Внимание: Применение немного изменилось. Теперь для того чтобы использовать генератор, требуется создать его экземпляр и уже у него вызывать методы.