我尝试创建一个很好的可测试存储库类以与Moq一起使用。我不想重复选择器方法(GetAll
,Get
,...)。我的实现工作正常,但是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
}
}
答案 0 :(得分:1)
如果您只想测试基类,那么我将创建该类的单元测试特定实现,并仅提供任何帮助程序功能来测试受保护的功能。基本上,您在测试类中以MyAreaRepository
做过,但是作为private class
做过。
答案 1 :(得分: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();