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