Нужна помощь, чтобы лучше понять Moq

Я смотрел документацию Moq, и комментарии слишком короткие для меня, чтобы понять каждую из вещей, которые он может сделать.

Первое, что я не получаю, это It.IsAny<string>(). //example using string

Есть ли преимущество использования этого, просто введя какое-то значение? Я знаю, что люди говорят, что используют это, если вы не заботитесь о ценности, но если вы не заботитесь о ценности, вы не можете просто "что-то" или что-то еще? Это просто похоже на набирание текста.

Во-вторых, когда было бы примером того, когда вы не будете заботиться о стоимости? Я думал, что Moq нуждается в ценности для соответствия материалам.

Я не понимаю, для чего вообще It.Is<> или как его использовать. Я не понимаю пример и то, что он пытается показать.

Далее, мне не удается использовать Times (и его методы AtMost и т.д.). Почему вы ограничиваете количество раз, когда что-то настраивается? У меня есть значение AppConfig, которое мне нужно использовать дважды. Почему я хочу ограничить его, скажем, один раз? Это просто сделало бы тест неудачным. Это значит, чтобы другие люди не добавляли еще один код в код или что-то еще?

Я не понимаю, как использовать mock.SetupAllProperties(); Что он задает с помощью?

Я также не понимаю, почему существует так много разных способов настройки свойства и каковы их отличия. В документации есть:

SetupGet(of property)
SetupGet<TProperty>

Я заметил, что много вещей в Moq показывает () и <> - какая разница между ними и чем они будут выглядеть при использовании?

Я также не понимаю, почему они имеют SetupGet. Не могли бы вы использовать SetupSet для установки свойства? SetupSet имеет пять различных способов использования в документации. Плюс еще один, называемый SetupProperty. Поэтому я не понимаю, почему их так много.

На стороне примечания, мне интересно, не являются ли переменные, используемые в лямбдах, независимыми от других лямбда. Например:.

mock.setup(m => m.Test);
stop.setup(m => m.Test);

Будет ли это нормально или будет какой-то конфликт между переменной m?

Наконец, я был смотрел это видео, и мне интересно, показывает ли он Visual Studio. Его Intellisense выглядит по-другому. Для него появляется лампочка (я счастлив, что у меня нет, так как она возвращает болезненные воспоминания о неберах), и есть линии, идущие от одной открытой скобки до закрывающей скобки и т.д.

Спасибо:)

Ответ 1

It.IsAny/It.Is

Это может быть полезно, когда вы передаете новый ссылочный тип в тестируемый код. Например, если у вас есть метод по строкам: -

public void CreatePerson(string name, int age) {
    Person person = new Person(name, age);
    _personRepository.Add(person);
}

Возможно, вы захотите проверить, что метод add был вызван в репозитории

[Test]
public void Create_Person_Calls_Add_On_Repository () {
    Mock<IPersonRepository> mockRepository = new Mock<IPersonRepository>();
    PersonManager manager = new PersonManager(mockRepository.Object);
    manager.CreatePerson("Bob", 12);
    mockRepository.Verify(p => p.Add(It.IsAny<Person>()));
}

Если вы хотите сделать этот тест более явным, вы можете использовать It.Is, поставив предикат, который должен соответствовать объекту person

[Test]
public void Create_Person_Calls_Add_On_Repository () {
    Mock<IPersonRepository> mockRepository = new Mock<IPersonRepository>();
    PersonManager manager = new PersonManager(mockRepository.Object);
    manager.CreatePerson("Bob", 12);
    mockRepository.Verify(pr => pr.Add(It.Is<Person>(p => p.Age == 12)));
}

Таким образом, тест будет проходить через исключение, если объект person, который использовался для вызова метода добавления, не имел возрастного свойства, установленного в 12.

Времена

Если у вас был метод по строкам: -

public void PayPensionContribution(Person person) {
    if (person.Age > 65 || person.Age < 18) return;
    //Do some complex logic
    _pensionService.Pay(500M);
}

Одна из вещей, которые вы можете протестировать, заключается в том, что метод оплаты не вызывается, когда в метод передается человек старше 65 лет.

[Test]
public void Someone_over_65_does_not_pay_a_pension_contribution() {
    Mock<IPensionService> mockPensionService = new Mock<IPensionService>();
    Person p = new Person("test", 66);
    PensionCalculator calc = new PensionCalculator(mockPensionService.Object);
    calc.PayPensionContribution(p);
    mockPensionService.Verify(ps => ps.Pay(It.IsAny<decimal>()), Times.Never());
}

Аналогичным образом можно представить ситуации, в которых вы выполняете итерацию по коллекции и вызывая метод для каждого элемента в коллекции, и вы хотели бы удостовериться, что это было вызвано определенным количеством раз, иначе вы просто просто Не заботьтесь.

SetupGet/SetupSet

Что вам нужно знать с этими парнями, так это то, что они отражают то, как ваш код взаимодействует с макетом, а не как настроить макет

public static void SetAuditProperties(IAuditable auditable) {
    auditable.ModifiedBy = Thread.CurrentPrincipal.Identity.Name;
}

В этом случае код устанавливает свойство ModifiedBy экземпляра IAuditable, когда он получает свойство Name текущего экземпляра IPrincipal

[Test]
public void Accesses_Name_Of_Current_Principal_When_Setting_ModifiedBy() {
    Mock<IPrincipal> mockPrincipal = new Mock<IPrincipal>();
    Mock<IAuditable> mockAuditable = new Mock<IAuditable>();

    mockPrincipal.SetupGet(p => p.Identity.Name).Returns("test");

    Thread.CurrentPrincipal = mockPrincipal.Object;
    AuditManager.SetAuditProperties(mockAuditable.Object);

    mockPrincipal.VerifyGet(p => p.Identity.Name);
    mockAuditable.VerifySet(a => a.ModifiedBy = "test");
}

В этом случае мы настраиваем свойство name на макет IPrincipal, чтобы он возвращал "тест", когда геттер вызывается в свойстве Name Identity, который мы не устанавливаем сами.

SetupProperty/SetupAllProperties

Глядя на тест выше, если он был изменен на чтение

[Test]
public void Accesses_Name_Of_Current_Principal_When_Setting_ModifiedBy() {
    Mock<IPrincipal> mockPrincipal = new Mock<IPrincipal>();
    Mock<IAuditable> mockAuditable = new Mock<IAuditable>();
    mockPrincipal.SetupGet(p => p.Identity.Name).Returns("test");

    var auditable = mockAuditable.Object;

    Thread.CurrentPrincipal = mockPrincipal.Object;
    AuditManager.SetAuditProperties(auditable);

    Assert.AreEqual("test", auditable.ModifiedBy);
}

Тест завершится неудачно. Это связано с тем, что прокси, созданный Moq, на самом деле ничего не делает в установленном методе свойства, если вы его не скажете. В аффекте макет объекта выглядит примерно так:

public class AuditableMock : IAuditable {
     public string ModifiedBy { get { return null; } set { } }

} 

Чтобы пройти тест, вы должны сообщить Moq, чтобы настроить свойство на стандартное поведение свойства. Вы можете сделать это, вызвав SetupProperty, и макет будет больше похож на

public class AuditableMock : IAuditable {
     public string ModifiedBy { get; set; }
} 

и тест выше будет проходить, поскольку значение "тест" теперь будет храниться против макета. При запутывании сложных объектов вы можете сделать это для всех свойств, поэтому ярлык SetupAllProperties

Наконец, лампочка в IDE - это плагин resharper.

Ответ 2

Если вы не заботитесь о точном значении свойства, гораздо лучше использовать .IsAny, потому что вы четко указываете на то, что точное значение не имеет значения. Если вы жестко обозначили его как "abc", тогда неясно, зависит ли ваш код от запуска с "a" или заканчивается на "c" или длиной 3 символа и т.д. И т.д.