Использование Moq для переопределения виртуальных методов в одном классе - PullRequest
27 голосов
/ 22 августа 2010

Мы используем Moq для модульного тестирования наших классов обслуживания, но застряли на том, как тестировать ситуации, когда метод службы вызывает другой метод службы того же класса. Я попытался установить вызываемый метод на виртуальный, но все еще не мог понять, что делать тогда в 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, чтобы сделать это проще?

Ответы [ 3 ]

45 голосов
/ 22 августа 2010

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

    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 голосов
/ 11 ноября 2010
var mock = new Moq.Mock<RenewalService> { CallBase = true };
mock.Setup(m => m.GetNextRenewalDate(It.IsAny<Guid>())).Returns(null);
var results = mock.Object.IsLastRenewalOfYear(...);
1 голос
/ 22 августа 2010

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...