摘要 class 构造函数调用可覆盖方法
Abstract class constructor call overridable method
我尝试创建一个良好的可测试存储库 class 以与 Moq 一起使用。我不想重复我的选择器方法(GetAll
、Get
、...)。我的实现工作正常,但 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();
我尝试创建一个良好的可测试存储库 class 以与 Moq 一起使用。我不想重复我的选择器方法(GetAll
、Get
、...)。我的实现工作正常,但 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();