Подтвердить что ты не робот

Использование Moq для переопределения виртуальных методов в одном классе

Мы используем Moq для unit test наших классов обслуживания, но зацикливаемся на том, как тестировать ситуации, когда метод службы вызывает другой метод обслуживания того же класса. Я попытался настроить метод, вызываемый на виртуальный, но все еще не мог понять, что делать тогда в Moq. Например:

public class RenewalService : IRenewalService
{
    //we've already tested this
    public virtual DateTime? GetNextRenewalDate(Guid clientId)
    {
        DateTime? nextRenewalDate = null;
        //...<snip> a ton of already tested stuff...

        return nextRenewalDate;
    }

    //but want to test this without needing to mock all 
    //the methods called in the GetNextRenewalDate method
    public bool IsLastRenewalOfYear(Renewal renewal)
    {
        DateTime? nextRenewalDate = GetNextRenewalDate(renewal.Client.Id);
        if (nextRenewalDate == null)
            throw new Exceptions.DataIntegrityException("No scheduled renewal date, cannot determine if last renewal of year");
        if (nextRenewalDate.Value.Year != renewal.RenewDate.Year)
            return true;
        return false;
    }
}

В приведенном выше примере наш метод GetNextRenewalDate довольно сложный, и мы уже его тестировали. Однако мы хотим протестировать более простой IsLastRenewalOfYear без необходимости издеваться над всем необходимым для GetNextRenewalDate. В принципе, мы просто хотим высмеять GetNextRenewalDate.

Я понимаю, что могу создать новый класс, который переопределяет GetNextRenewalDate и тестирует новый класс, но есть ли способ, которым я могу использовать Moq, чтобы сделать это проще?

4b9b3361

Ответ 1

Вероятно, вы можете использовать частичное издевательство в этом сценарии, хотя все ваши методы должны быть виртуальными:

    var mock = new Moq.Mock<RenewalService>();
    mock.Setup(m => m.GetNextRenewalDate(It.IsAny<Guid>())).Returns(null);
    mock.CallBase = true;
    var results = mock.Object.IsLastRenewalOfYear(...);

Ответ 2

Изменить. Теперь я согласен, что это неправильный ответ для случая Эндрю. Я хочу оставить этот ответ здесь для комментариев. Пожалуйста, не голосуйте больше:)

Перед редактированием:

В общем, фреймворки объектного дизайна не предназначены для упрощения сценария одного класса, они предназначены для изоляции вашего кода, поэтому вы можете протестировать один класс.

Если вы попытаетесь использовать инфраструктуру макета для решения этой проблемы, инфраструктура просто собирается создать производный класс и перегрузить этот метод. Единственное различие в том, что вы можете сделать это примерно в 3 строках вместо 5, потому что вам не нужно создавать определение производного класса.

Если вы хотите использовать макетные объекты, чтобы изолировать это поведение, вам нужно немного разбить этот класс. Логика GetNextRenewalDate может находиться вне объекта RenewalService.

Тот факт, что вы столкнулись с этой проблемой, может показать, что есть еще более простой или более мелкозернистый дизайн, который еще предстоит обнаружить. Поиск класса, который является немного менее конкретным, с именем "менеджер" или "сервис" часто является подсказкой о том, что вы можете разбить свой дизайн на более мелкие классы и получить возможность повторного использования и поддержки из него.

Ответ 3

var mock = new Moq.Mock<RenewalService> { CallBase = true };
mock.Setup(m => m.GetNextRenewalDate(It.IsAny<Guid>())).Returns(null);
var results = mock.Object.IsLastRenewalOfYear(...);