2015-08-25 4 views
1

Я хочу высмеять единицу рабочего интерфейса с репозиториями внутри для целей единичного тестирования. Пока я могу сделать это, как показано ниже.Как издеваться над IRepository <T>?

namespace Liquid.Service.UnitTest 
{ 
    using Liquid.DataAccess.Interface; 
    using Liquid.Domain; 
    using Domain.Interface; 
    using Moq; 
    using System.Collections.Generic; 
    using System.Linq; 

    internal class Helper 
    { 
     internal Mock<IUnitOfWork> MockUnitOfWork(ICollection<Dummy> dummies = null, 
      ICollection<ProductType> productTypes = null) 
     { 
      dummies = dummies ?? new List<Dummy>(); 
      productTypes = productTypes ?? new List<ProductType>(); 

      var dummyRepositoryMock = MockDummyRepository(dummies); 
      var productTypeRepositoryMock = MockProductTypeRepository(productTypes); 

      var unitOfWorkMock = new Mock<IUnitOfWork>(); 
      unitOfWorkMock.Setup(x => x.DummyRepository) 
       .Returns(dummyRepositoryMock.Object); 
      unitOfWorkMock.Setup(x => x.ProductTypeRepository) 
       .Returns(productTypeRepositoryMock.Object); 

      return unitOfWorkMock; 
     } 

     private Mock<IDummyRepository> MockDummyRepository(ICollection<Dummy> dummies) 
     { 
      var dummyRepositoryMock = new Mock<IDummyRepository>(); 

      dummyRepositoryMock.Setup(x => x.FindById(It.IsAny<int>())) 
       .Returns((int arg1) => dummies.Where(x => x.Id == arg1).SingleOrDefault()); 

      dummyRepositoryMock.Setup(x => x.Add(It.IsAny<Dummy>())) 
       .Callback((Dummy arg1) => dummies.Add(arg1)); 

      return dummyRepositoryMock; 
     } 

     private Mock<IProductTypeRepository> MockProductTypeRepository(ICollection<ProductType> productTypes) 
     { 
      var productTypeRepositoryMock = new Mock<IProductTypeRepository>(); 

      productTypeRepositoryMock.Setup(x => x.FindById(It.IsAny<int>())) 
       .Returns((int arg1) => productTypes.SingleOrDefault(x => x.Id == arg1)); 

      productTypeRepositoryMock.Setup(x => x.Add(It.IsAny<ProductType>())) 
       .Callback((ProductType arg1) => productTypes.Add(arg1)); 

      return productTypeRepositoryMock; 
     } 
    } 
} 

Вы видите, что я создал два метода издеваться DummyRepository и ProductTypeRepository, но потому, что он имеет ту же реализацию, я думаю, что это является излишним для каждого репозиториев у меня есть. Ниже приведены коды репозиториев и IRepository.

namespace Liquid.DataAccess.Interface 
{ 
    using Liquid.Domain; 
    using System.Collections.Generic; 
    using System.Threading; 
    using System.Threading.Tasks; 

    public interface IDummyRepository : IRepository<Dummy> 
    { 
    } 

    public interface IProductTypeRepository : IRepository<ProductType> 
    { 
    } 

    public interface IRepository<TEntity> where TEntity : class 
    { 
     IList<TEntity> GetAll(); 

     Task<List<TEntity>> GetAllAsync(); 

     Task<List<TEntity>> GetAllAsync(CancellationToken cancellationToken); 

     IList<TEntity> PageAll(int skip, int take); 

     Task<List<TEntity>> PageAllAsync(int skip, int take); 

     Task<List<TEntity>> PageAllAsync(CancellationToken cancellationToken, int skip, int take); 

     TEntity FindById(object id); 

     Task<TEntity> FindByIdAsync(object id); 

     Task<TEntity> FindByIdAsync(CancellationToken cancellationToken, object id); 

     void Add(TEntity entity); 

     void Update(TEntity entity); 

     void Remove(TEntity entity); 
    } 
} 

Как я могу использовать тот же метод, чтобы высмеивать каждую реализацию репозиториев, который наследуется IRepository?

ОБНОВЛЕНИЕ: Тест - это просто добавление и проверка, как показано ниже.

[Test] 
    public void ProductTypeService_Add() 
    { 
     // GIVEN 
     var productTypeData = new ProductType() 
     { 
      Id = 1, 
      Description = "ProductType1" 
     }; 

     // WHEN 
     var unitOfWorkMock = new Helper().MockUnitOfWork(); 
     var productTypeService = new ProductTypeService(unitOfWorkMock.Object); 
     productTypeService.Add(productTypeData); 
     unitOfWorkMock.Verify(x => x.SaveChanges()); 

     // THEN 
     Assert.That(productTypeService.FindById(1) != null); 
     Assert.That(productTypeService.FindById(2) == null); 

     // WHEN 
     var productTypeData2 = new ProductType() 
     { 
      Id = 2, 
      Description = "ProductType2" 
     }; 

     productTypeService.Add(productTypeData2); 

     // THEN 
     Assert.That(productTypeService.FindById(2) != null); 
    } 
+0

Как вы можете сделать это один метод? Вы четко используете разные свойства в каждой настройке. В одном вы используете _dummies и в одном вы используете _productTypes. Можете ли вы изменить их для использования общего свойства, например. Пункты, а затем мы можем помочь? – Ruskin

+0

@ Ruskin Да, это проблема, а также я тоже не могу понять :) Как я могу использовать общее поле, если объекты разные? Возможно, я могу использовать общий тип, но я новичок в этом и не могу заставить его работать. – Hendry

+0

Хорошо, есть решение, но почему вы добавляете предметы из макета в свои частные коллекции? – Ruskin

ответ

1

Я думаю, что вы усложнили свой вопрос и, следовательно, свое решение. Вам не нужны интерфейсы для ваших различных хранилищ, например IDummyRepository и IProductRepository, если вы не добавляете к нему никакого значения.

Ваши данные классы

public class Dummy 
{ 
    public int Id { get; set; } 
} 

public class ProductType 
{ 
    public int Id { get; set; } 
} 

Ваш ProductTypeService (я могу только предположить это)

public class ProductTypeService 
{ 
    private readonly IUnitOfWork _unitOfWork; 

    public ProductTypeService(IUnitOfWork unitOfWork) 
    { 
     _unitOfWork = unitOfWork; 
    } 

    public void AddProductType(ProductType productType) 
    { 
     _unitOfWork.ProductTypes.Add(productType); 
    } 
} 

Ваш IUnitOfWork

public interface IUnitOfWork 
{ 
    IRepository<Dummy> Dummies { get; set; } 
    IRepository<ProductType> ProductTypes { get; set; } 
} 

Ваш IRepository остается неизменным, так что я не буду копировать вставить это здесь!

Наконец ваш блок тест

[TestFixture] 
public class Class1 
{ 
    private Mock<IUnitOfWork> _unitOfWorkMock; 
    private Mock<IRepository<Dummy>> _dummyRepositoryMock; 
    private Mock<IRepository<ProductType>> _productTypeRepositoryMock; 

    [SetUp] 
    public void Setup() 
    { 
     _unitOfWorkMock = new Mock<IUnitOfWork>(); 
     _dummyRepositoryMock = CreateMock<Dummy>(); 
     _productTypeRepositoryMock = CreateMock<ProductType>(); 

     _unitOfWorkMock.Setup(u => u.Dummies).Returns(_dummyRepositoryMock.Object); 
     _unitOfWorkMock.Setup(u => u.ProductTypes).Returns(_productTypeRepositoryMock.Object); 
    } 

    [Test] 
    public void product_type_service_should_add_item_to_the_underlying_repository() 
    { 
     var productTypeService = new ProductTypeService(_unitOfWorkMock.Object); 
     var productType = new ProductType {Id = 10}; 
     productTypeService.AddProductType(productType); 
     _productTypeRepositoryMock.Verify(r => r.Add(It.Is<ProductType>(p => p.Id == productType.Id)), Times.Once()); 
    } 

    private Mock<IRepository<T>> CreateMock<T>() where T : class 
    { 
     var mock = new Mock<IRepository<T>>(); 

     // do more common magic here 

     return mock; 
    } 
} 
+0

. Я думаю, что у вас есть точка на сложном уровне репозитория в отношении YAGNI. Я изменил свой уровень репозитория с помощью IRepository и выполнил ваш ответ. Вы также даете мне некоторое представление о том, как я должен тестировать. Большое спасибо! – Hendry

3

ИМХО, вы проверяете неправильную вещь; а именно, вы проверяете, что коллекция в памяти (List<T>) может хранить данные, а данные можно найти в коллекции. Это всегда дает истину, потому что это назначение коллекций в памяти.

Вместо того, чтобы делать это нужно либо создать интеграционные тесты, которые будут использовать фактическую реализацию базового хранилища (например, как Entity Framework) или просто протестировать поведение вашей службы, как это:

[Test] 
public void ProductTypeService_WhenAddingNewItem_CallsSaveChanges() 
{ 
    var unitOfWork = new Mock<IUnitOfWork>(); 
    // setup the properties of the mock here... 

    var service = new ProductTypeService(unitOfWork); 
    service.Add(new ProductType { Id = 2, Description = "some product" }); 

    unitOfWork.AssertWasCalled(_ => _.SaveChanges()); 
} 

Таким образом, вы проверяете, что ваш сервис вызывает метод SaveChanges(); фактически сохранение данных является обязанностью репозитория, и, как я сказал выше, тестирование того, что список может хранить данные в памяти, бесполезно.