【问题标题】:Unit testing concrete services extending generic service单元测试扩展通用服务的具体服务
【发布时间】:2021-02-09 15:51:31
【问题描述】:

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

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 添加无参数构造函数并运行测试会发生什么?
  • 为什么 AddressService 期望 BaseRepository 作为输入而不是 IBaseRepository?改变它应该可以解决你的问题(当然你应该模拟IBaseRepository
  • 为了完成单元测试而添加无参数构造函数是一种好习惯吗?当我添加它时,它在BaseRepository 中抛出了一个System.NullReferenceException: 'Object reference not set to an instance of an object.',因为_dbContext 为空,并且对数据库的每个操作都需要该属性,因为在那一侧没有任何东西被模拟
  • 我在其他地方看到了问题,尝试模拟 DatabaseContext(先用接口包装它)。

标签: c# .net unit-testing asp.net-core xunit


【解决方案1】:

在我看来,您正在尝试利用依赖注入的好处,这很好。但是,目前您使用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);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-07
    • 1970-01-01
    • 1970-01-01
    • 2011-02-03
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多