49

我一直在查看 Moq 文档,但评论太短了,我无法理解它可以做的每一件事。

我不明白的第一件事是It.IsAny<string>(). //example using string

使用它比仅仅投入一些价值有优势吗?我知道人们说如果你不关心价值就使用这个,但如果你不关心价值,你不能只做“a”之类的吗?这似乎更像是打字。

其次,什么时候是你不关心价值的例子?我认为起订量需要价值来匹配东西。

我根本不明白It.Is<>它的用途或如何使用它。我不明白这个例子以及它试图展示的内容。

接下来,我不知道何时使用Times(以及它的AtMost方法和类似的)。为什么要限制设置的次数?我有一些AppConfig价值需要使用两次。为什么我要将它限制为,比如说,一次?这只会使测试失败。这是为了阻止其他人在您的代码中添加另一个代码还是什么?

我不明白如何使用mock.SetupAllProperties(); 它设置属性的内容是什么?

我也不明白为什么有这么多不同的方式来设置属性以及它们的区别是什么。文档有:

SetupGet(of property)
SetupGet<TProperty>

我注意到 Moq 中的很多东西都显示了()-<>它们之间有什么区别,它们在使用中会是什么样子?

我也不明白为什么他们有SetupGet。你不会用SetupSet设置属性吗? SetupSet在文档中有五种不同的使用方式。再加上另一个叫SetupProperty. 所以我不明白为什么有这么多。

在旁注中,我想知道 lambdas 中使用的变量是否独立于其他 lambdas。例如:

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

这可以吗,还是变量之间会有一些冲突m

最后,我在看这个视频,我想知道它是否显示了 Visual Studio。他的 Intellisense 看起来不一样。一个灯泡为他弹出(我很高兴我的没有,因为它带回了对netbeans的痛苦回忆),并且从一个开口支架到闭合支架等都有线条。

4

2 回答 2

113

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)));
}

这样,如果用于调用 add 方法的 person 对象没有将 age 属性设置为 ,则测试将通过异常12

时代

如果您有以下方法:-

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

您可能要测试的一件事是,当将 65 岁以上的人传递给方法时,不会调用 pay 方法

[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());
}

类似地,可以想象这样的情况,您正在迭代一个集合并为集合中的每个项目调用一个方法,并且您希望确保它被调用了一定次数,其他时候您根本不关心。

设置获取 / 设置设置

对于这些人,您需要注意的是它们反映了您的代码如何与模拟交互,而不是您如何设置模拟

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

在这种情况下,代码设置 IAuditable 实例的 ModifiedBy 属性,同时获取 IPrincipal 的当前实例的 Name 属性,

[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");
}

在这种情况下,我们在 IPrincipal 的模拟上设置 name 属性,因此当在 Identity 的 Name 属性上调用 getter 时它返回“test”,我们没有设置属性本身。

设置属性/设置所有属性

看上面的测试如果改成阅读

[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 创建的代理实际上并没有在属性的 set 方法中执行任何操作,除非您告诉它这样做。实际上,模拟对象看起来有点像这样

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

} 

要使测试通过,您必须告诉 Moq 将属性设置为具有标准属性行为。您可以通过调用 SetupProperty 来做到这一点,模拟看起来更像

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

并且上面的测试将通过,因为值“test”现在将针对模拟存储。模拟复杂对象时,您可能希望对所有属性执行此操作,因此使用 SetupAllProperties 快捷方式

最后,IDE 中的灯泡是 ReSharper 插件。

于 2009-07-03T20:47:04.423 回答
5

如果您不关心属性的确切值,那么使用 .IsAny 会更好,因为您明确指出确切值并不重要这一事实。如果您将其硬编码为“abc”,则不清楚您正在测试的代码是否依赖于以“a”开头或以“c”结尾或 3 个字符长等。

于 2009-07-02T14:26:02.867 回答