我建立的基础设施是基于通用服务和通用存储库的。现在我正在尝试编写单元测试,并且遇到了一些挑战。下面是代码:
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
}
BaseRepository:
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;
}
}
ServiceBase:
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);
}
}