Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Большинство приложений так или иначе взаимодействуют с базой данных. Посмотрим, как мы можем протестировать данный аспект.
И вначале создадим новый проект, и вместе с ним проект тестов:
Затем определим в проекте модель Computer и класс контекста CompContext, с помощью которых идет взаимодействие с базой данных:
// модель public class Computer { public int Id { get; set; } public string Name { get; set; } } // контекст public class CompContext : DbContext { public DbSet<Computer> Computers { get; set; } }
Теперь в соответствии с принципами TDD сначала напишем тест, который будет проверять модель представления. В проекте тестов изменим класс HomeControllerTest следующим образом:
[TestClass] public class HomeControllerTest { [TestMethod] public void IndexViewModelNotNull() { // Arrange HomeController controller = new HomeController(); // Act ViewResult result = controller.Index() as ViewResult; // Assert Assert.IsNotNull(result.Model); } }
Здесь проверяем модель, которую передаем из базы данных в представление.
Запустим тест на выполнение и увидим, что он естественно заканчивается неудачей, так как в методе Index мы еще ничего не получаем из бд и не передаем в представление.
Теперь пишем минимальный код, в самом контроллере, который производит это действие. Для этого изменим контроллер HomeController следующим образом:
public class HomeController : Controller { CompContext db = new CompContext(); public ActionResult Index() { return View(db.Computers); } }
Перестроим проект и снова запустим тесты. Теперь зеленый цвет тестов сигнализирует об их успешном окончании. Однако тут есть одна проблема. Такой метод сложно тестировать в силу жесткой связи с базой данных. Если что-то случится с базой данных, и она окажется недоступна, тогда тест завершится неудачей. Но это не значит, что что-то не так в самом коде или что логика построена неправильно.
Поэтому таких ситуаций обычно избегают. И для этого вместо жесткой связи объектов используют слабосвязанные объекты (loosely coupled objects).
Непосредственно в данном случае проблема решается перенесением уровня взаимодействия с бд в специальный класс, который представляет реализацию паттерна репозиторий.
Для этого добавим в главный проект новый интерфейс и класс, его реализующий:
public interface IRepository : IDisposable { List<Computer> GetComputerList(); Computer GetComputer(int id); void Create(Computer item); void Update(Computer item); void Delete(int id); void Save(); } public class ComputerRepository : IRepository { private CompContext db; public ComputerRepository() { this.db = new CompContext(); } public List<Computer> GetComputerList() { return db.Computers.ToList(); } public Computer GetComputer(int id) { return db.Computers.Find(id); } public void Create(Computer c) { db.Computers.Add(c); } public void Update(Computer c) { db.Entry(c).State = EntityState.Modified; } public void Delete(int id) { Computer c = db.Computers.Find(id); if(c!=null) db.Computers.Remove(c); } public void Save() { db.SaveChanges(); } private bool disposed = false; public virtual void Dispose(bool disposing) { if(!this.disposed) { if(disposing) { db.Dispose(); } } this.disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }
Теперь в контроллере установим взаимодействие с базой данных через репозиторий:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using MoqMvcApp.Models; namespace MoqMvcApp.Controllers { public class HomeController : Controller { IRepository repo; public HomeController(IRepository r) { repo = r; } public HomeController() { repo = new ComputerRepository(); } public ActionResult Index() { return View(); } protected override void Dispose(bool disposing) { repo.Dispose(); base.Dispose(disposing); } } }
Теперь мы избавились от жесткой связи с базой данных. Но теперь нам надо будет в тестах сымитировать объект репозитория. И в этом нам поможет Moq-фреймворк.