单元测试扩展通用服务的具体服务

Unit testing concrete services extending generic service

我建立的基础设施是基于通用服务和通用存储库的。现在我正在尝试编写单元测试,但遇到了一些挑战。这是代码:

IBaseRepository:

public interface IBaseRepository<T> where T : class
{
    IEnumerable<T> GetAll();
    T Add(T entity, bool saveChanges = true);
    //more generic code
}

IServiceBase:

public interface IServiceBase<TEntity>
{
    IEnumerable<TEntity> GetAll();
    TEntity Create(TEntity entity);
    //more generic code
}

基础资料库:

public class BaseRepository<T> : IBaseRepository<T> where T : class
{
    private readonly DatabaseContext _dbContext;

    public BaseRepository(DatabaseContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IEnumerable<T> GetAll()
    {
        return _dbContext.Set<T>();
    }

    public T Add(T entity, bool saveChanges = true)
    {
        _dbContext.Set<T>().Add(entity);
        if (saveChanges) _dbContext.SaveChanges();
        return entity;
    }
}

服务基地:

public abstract class ServiceBase<TEntity, TRepository> : IServiceBase<TEntity>
        where TEntity : class
        where TRepository : BaseRepository<TEntity>
{
    public TRepository Repository;

    public ServiceBase(BaseRepository<TEntity> rep)
    {
        Repository = (TRepository)rep;
    }

    public long Count(Expression<Func<TEntity, bool>> whereCondition)
    {
        return Repository.GetAll().AsQueryable().Where(whereCondition).Count();
    }
}

AddressService(具体域服务):

 public class AddressService : ServiceBase<Address, BaseRepository<Address>>, IAddressService
 {
    public AddressService(BaseRepository<Address> rep) : base(rep)
    {
    }

    public VerifyAddress()
    {
        //custom logic...
    }
 }

测试:

public class AddressTests
{
    [Fact]
    public void VerifyAddress()
    {
        var baseRepository = new Mock<BaseRepository<Address>>();
        var addressService = new AddressService(baseRepository.Object);            

        var test = addressService.Verify();
        Assert.NotNull(test);
    }
}

我得到的错误是Message: Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Repositories.Repositories.BaseRepository. Could not find a parameterless constructor.

我知道为什么会收到此错误。原因是因为 BaseRepository 具有期望 DatabaseContext 作为参数的构造函数。

我的问题是是否可以使用这样的设置进行单元测试 AddressService

在我看来,您正在尝试利用依赖注入的好处,这很好。但是,目前您使用 BaseRepository 作为依赖项而不是 IBaseRepository 这使得 IBaseRepository 有点多余并将您的 类 与特定实现紧密耦合(BaseRepository).这也意味着您对 DatabaseContext 有很强的依赖性,因为它是实例化 BaseRepository 所必需的。如果你后退一步并利用 IBaseRepository 进行更松散的耦合,你将同时避免对 DatabaseContext 的依赖,从而使单元测试也更容易。

首先,更改 ServiceBase 的定义:

public abstract class ServiceBase<TEntity, TRepository> : IServiceBase<TEntity>
        where TEntity : class
        where TRepository : IBaseRepository<TEntity>
{
    public TRepository Repository { get; private set; }

    public ServiceBase(TRepository rep)
    {
        Repository = rep;
    }
    // ...
}

然后 AddressService:

public class AddressService : ServiceBase<Address, IBaseRepository<Address>>, IAddressService
{
    public AddressService(IBaseRepository<Address> rep) : base(rep)
    {
    }
    // ...
}

现在您不再依赖 DatabaseContext 并且可以更简单地进行单元测试 AddressService(与您计划的方式相同):

public class AddressTests
{
    [Fact]
    public void VerifyAddress()
    {
        var baseRepository = new Mock<IBaseRepository<Address>>();
        var addressService = new AddressService(baseRepository.Object);            

        var test = addressService.Verify();
        Assert.NotNull(test);
    }
}