Абстрактный конструктор класса вызывает переопределяемый метод - PullRequest
1 голос
/ 15 апреля 2019

Я пытаюсь создать хороший тестируемый класс репозитория для использования с 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
    }
}

Ответы [ 2 ]

1 голос
/ 16 апреля 2019

Конструкторы 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();
1 голос
/ 15 апреля 2019

Если вы хотите протестировать только базовый класс, то я бы создал реализацию класса, специфичную для модульного теста, и просто предоставил бы все вспомогательные функции для тестирования защищенных. В основном то, что вы сделали с MyAreaRepository, но как private class в тестовом классе.

...