Слабосвязанные объекты и тестирование работы с БД

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 31.10.2015

Большинство приложений так или иначе взаимодействуют с базой данных. Посмотрим, как мы можем протестировать данный аспект.

И вначале создадим новый проект, и вместе с ним проект тестов:

Затем определим в проекте модель 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-фреймворк.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850