【问题标题】:Abstract class constructor call overridable method抽象类构造函数调用可重写方法
【发布时间】:2019-04-15 15:45:28
【问题描述】:

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

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

基类

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
    }
}

【问题讨论】:

  • 您是否打算覆盖除Initialize 以外的任何内容? Initialize 从哪里获取数据?
  • @ScottHannen 我现在只覆盖 Initialize,数据可以来自数据库或 web 服务

标签: c# moq abstract-base-class


【解决方案1】:

RSPEC-1699 构造函数应该只调用不可覆盖的方法在单元测试中没有任何内容,无论您将如何测试它,它都将保留在那里。

有人知道更好的实现方式吗?

我想提出另一种方法以避免这种违规行为并使您的代码更具可测试性。

这个想法是代替base类使用组合和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();

【讨论】:

    【解决方案2】:

    如果您只想测试基类,那么我将创建该类的单元测试特定实现,并提供任何帮助函数来测试受保护的函数。基本上你用MyAreaRepository 做了什么,但在测试类中作为private class

    【讨论】:

    • 不,我想测试另一个依赖于基类的类
    • 我仍然会做同样的伎俩,只是从 MyAreaRepository 派生您的测试类,并用一些模拟数据覆盖 Initialize() 函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-14
    • 2020-06-18
    • 2016-08-20
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    相关资源
    最近更新 更多