摘要 class 构造函数调用可覆盖方法

Abstract class constructor call overridable method

我尝试创建一个良好的可测试存储库 class 以与 Moq 一起使用。我不想重复我的选择器方法(GetAllGet、...)。我的实现工作正常,但 SonarSource 报告错误 RSPEC-1699 有人知道更好的实现吗?

var areas = new Area[] { ... };
var areaRepositoryMock = new Mock<BaseAreaRepository>() { CallBase = true };
areaRepositoryMock.Setup(m => m.Initialize()).Returns(areas);

基础Class

public abstract class BaseAreaRepository
{
    protected Area[] _areas;

    protected BaseAreaRepository()
    {
        this._areas = this.Initialize();
    }

    public abstract Area[] Initialize();

    public Area[] GetAll()
    {
        return this._monitoredAreas;
    }

    public Area Get(int id)
    {
        return this._areas.FirstOrDefault(o => o.Id.Equals(id));
    }
}

MyAreaRepository

public class MyAreaRepository : BaseAreaRepository
{
    public override Area[] Initialize()
    {
        return //Load data from an other source
    }
}

如果您只想测试基础 class,那么我会创建一个 class 的单元测试特定实现,并只提供任何辅助函数来测试受保护的函数。基本上你用 MyAreaRepository 做了什么,但在测试 class.

中作为 private class

RSPEC-1699 构造函数应该只调用不可重写的方法 与单元测试没有任何关系,无论您如何进行,它都会保留在那里测试它。

Does anyone know of a better implementation?

我想提出另一种方法来避免这种违规行为并使您的代码更易于测试。

这个想法是代替 base class 使用组合和 DI 原则。

public interface IAreaContext
{
    Area[] GetAreas();
}

public class AreaRepository
{
    private IAreaContext _areaContext;

    protected BaseAreaRepository(IAreaContext areaContext)
    {
        _areaContext = areaContext;
    }

    public Area[] GetAll()
    {
        return _areaContext.GetAreas();
    }
}

然后你可以定义 IAreaContext 和 injext:

的多个实现
public class MyAreaContext : IAreaContext
{
    public Area[] GetAreas()
    {
        return //Load data from an other source
    }
}

public class MyOtherAreaContext : IAreaContext
{
    public Area[] GetAreas()
    {
        return //Load data from an other source
    }
}

现在,当您拥有此设置存储库时,可以轻松测试上下文本身的不同行为。这只是一个演示想法的例子:

//Arrange
var context = new Mock<IAreaContext>();
context.Setup(m => m.GetAreas()).Verifiable();
var sut = new AreaRepository(context.Object);

//Act
var _ = sut.GetAll();

//Assert
context.Verify();