jeudi 5 mai 2016

Mocking IUnitOfWork with inner Repositories by calling Mock.Verify method of inner repository

I have a project. It is simple License Manager. I have such DbContext (Entity Framework 5):

public class EFDbContext : DbContext
{
    public virtual IDbSet<License> Licenses { get; set; }
    public virtual IDbSet<Department> Departments { get; set; }
    public virtual IDbSet<Its> Itses { get; set; }
}

I created an IUnitOfWork implementation:

public class UnitOfWork : IUnitOfWork
{
    private readonly EFDbContext Db;
    private readonly LicenseRepository LicenseRepository;
    private readonly DepartmentRepository DepartmentRepository;
    private readonly ItsRepository ItsRepository;

    public UnitOfWork(EFDbContext context)
    {
        Db = context;
        DepartmentRepository = DepartmentRepository ?? (DepartmentRepository = new DepartmentRepository(Db));
        ItsRepository = ItsRepository ?? (ItsRepository = new ItsRepository(Db));
        LicenseRepository = LicenseRepository ?? (LicenseRepository = new LicenseRepository(Db));
    }

    public IRepository<License> Licenses
    {
        get { return LicenseRepository; }
    }

    public IRepository<Department> Departments
    {
        get { return DepartmentRepository; }
    }

    public IRepository<Its> Itses
    {
        get { return ItsRepository; }
    }
}

And I have an IRepository implementation named LicenseRepository:

public class LicenseRepository : IRepository<License> 
{
    private EFDbContext Db;

    public LicenseRepository(EFDbContext context)
    {
        Db = context;
    }

    public IQueryable<License> Items
    {
        get { return Db.Licenses.Include(lic => lic.Department).Include(lic => lic.Its); }
    }

    private bool CheckIfExist(string codeAct, int id)
    {
        return (id == 0)
                   ? Db.Licenses.Any(license => license.CodeAct.Equals(codeAct))
                   : Db.Licenses.Any(license => license.CodeAct.Equals(codeAct) && license.Id != id);
    }

    private void Defend(License item)
    {
        Security sec = Security.Instance;
        Its its = Db.Itses.FirstOrDefault(i => i.Id == item.ItsId);
        item.CodeLic = item.CodeLic.ToUpper()/*.Replace("-", " - ")*/;
        string hash = sec.GetHash(item.CodeLic.Replace(" - ", ""), (its == null) ? string.Empty : its.Salt);
        item.CodeAct = hash.Insert(5," - ");
    }

    public IQueryable<License> GetOrderBy<TResult>(string order, IQueryable<License> items, Expression<Func<License, TResult>> key)
    {
        return (order == null || order.ToUpper() == "ASC") ? items.OrderBy(key) : items.OrderByDescending(key);
    }

    public void Edit(License item)
    {
        Defend(item);
        if (CheckIfExist(item.CodeAct, item.Id)) throw new DbUpdateException("Error update");
        if (item.Id == 0)
        {
            Db.Licenses.Add(item);
        }
        else
        {
            License dbEntry = Db.Licenses.Find(item.Id);
            if (dbEntry != null)
            {
                dbEntry.CodeLic = item.CodeLic;
                dbEntry.CodeAct = item.CodeAct;
                dbEntry.Peom = item.Peom;
                dbEntry.Phone = item.Phone;
                dbEntry.Pib = item.Pib;
                dbEntry.DepartmentId = item.DepartmentId;
                dbEntry.ItsId = item.ItsId;
                dbEntry.Dtime = item.Dtime;
            }
        }
        Db.SaveChanges();
    }

    public License Delete(int id)
    {
        License dbEntry = Db.Licenses.Find(id);
        if (dbEntry != null)
        {
            Db.Licenses.Remove(dbEntry);
            Db.SaveChanges();
        }
        return dbEntry;
    }
}

My LicenseController looks like:

public class LicenseController: Controller
{
    private IUnitOfWork Repository;
    public LicenseController(IUnitOfWork _repository)
    {
        Repository = _repository;
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(LicenseViewModel model)
    {
        if (ModelState.IsValid) // валидация данных
        {
            model.License.DepartmentId = model.License.DepartmentId == 0 ? null : model.License.DepartmentId;
            model.License.ItsId = model.License.ItsId == 0 ? null : model.License.ItsId;
            if (model.License.Dtime == DateTime.MinValue)
            {
                model.License.Dtime = DateTime.Now;
            }
            model.License.CodeLic = string.Join(" - ", model.CodePart1, model.CodePart2, model.CodePart3,
                                                model.CodePart4);
            Repository.Licenses.Edit(model.License);
            // хранится, пока мы их не прочитаем (ViewBag(не может хранить дольше текущего запроса) и Session(требует явного удаления) нам не подходят)
            TempData["message"] = string.Format("Об'єкт \"{0}\" збережений", model.License.CodeLic);
            return RedirectToAction("List");
        }

        // в случае неудачи
        model.DropDownDepartment = GetDepartments(model.License.DepartmentId);
        model.DropDownIts = GetItses(model.License.ItsId);
        return View(model);
    }

    // another methods ...
}

I want to test if Edit method of Repository calls correctly when I pass correct Entity to it. My Mock class looks like:

private IDbSet<T> GetQueryableMockDbSet<T>(IQueryable<T> sourceList) where T: class
{
    Mock<IDbSet<T>> mockSet = new Mock<IDbSet<T>>();
    mockSet.As<IQueryable<T>>().Setup(t => t.Provider).Returns(sourceList.Provider);
    mockSet.As<IQueryable<T>>().Setup(t => t.Expression).Returns(sourceList.Expression);
    mockSet.As<IQueryable<T>>().Setup(t => t.ElementType).Returns(sourceList.ElementType);
    mockSet.As<IQueryable<T>>().Setup(t => t.GetEnumerator()).Returns(sourceList.GetEnumerator());
    return mockSet.Object;
}

[TestMethod]
public void CanSaveValidLicense()
{
    // ARRANGE
    // создали пустое имитированное хранилище
    var mockContext = new Mock<EFDbContext>();
    IUnitOfWork iuow = GetFakeRepository();
    mockContext.Setup(context => context.Itses).Returns(GetQueryableMockDbSet(iuow.Itses.Items));
    mockContext.Setup(context => context.Departments).Returns(GetQueryableMockDbSet(iuow.Departments.Items));
    mockContext.Setup(context => context.Licenses).Returns(GetQueryableMockDbSet(iuow.Licenses.Items));
    var mockRepo = new Mock<UnitOfWork>(mockContext.Object);
    mockRepo.CallBase = true;
    // создаем контроллер
    LicenseController target = new LicenseController(mockRepo.Object);
    // создаем объект типа License
    LicenseViewModel lvm = new LicenseViewModel
        {
            License = new License
                {
                    CodeAct = "ABCD",
                    CodeLic = "DCBA",
                    Peom = "111",
                    Phone = "777",
                    Pib = "BDV",
                    DepartmentId = 1,
                    ItsId = 1
                }
        };

    // ACT
    // пытаемся сохранить новосозданный объект
    ActionResult result = target.Edit(lvm);

    // ASSERT
    // проверяем, что был вызван метод хранилища на сохранение
    //mockContext.Verify(context => context.SaveChanges());
    //licRep.Verify(repository => repository.Edit(lvm.License), Times.Once());
    mockRepo.As<IRepository<License>>().Verify(work => work.Edit(lvm.License), Times.Once());
    mockRepo.VerifyAll();
    // проверяем, что сохранение прошло успешно - произошло перенаправление на страницу (не остались на текущей)
    Assert.IsNotInstanceOfType(result, typeof (ViewResult));
}

I have a headache with Verify method. It throws NotSupportedException or if I try another way it throws another Exception. I'm new in unit-testing. So, can you please help me with this problem.

Aucun commentaire:

Enregistrer un commentaire