需要帮助更好地了解Moq

时间:2009-07-02 11:31:58

标签: c# mocking lambda 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。所以我不明白为什么会这么多。

另一方面,我想知道lambdas中使用的变量是否独立于其他lambdas。 E.g:

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

这是否可以,或者变量m

之间是否存在冲突

最后,我是watching this video,我想知道它是否显示Visual Studio。他的Intellisense看起来与众不同。一个灯泡为他弹出(我很高兴我没有,因为它带回了netbeans的痛苦回忆),并且有一条线从一个开口支撑到闭合支撑等。

谢谢:)

2 个答案:

答案 0 :(得分:104)

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

如果你想让这个测试更明确,你可以通过提供person对象必须匹配的谓词来使用It.Is

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

同样地,你可以想象你正在迭代一个集合并为集合中的每个项目调用一个方法的情况,你想确保它被调用了一定次数,有时候你只是不在乎。

SetupGet / SetupSet

你需要注意的是,他们反映了你的代码与模拟的交互方式,而不是你如何设置模拟

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”,我们没有设置属性本身。

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

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

} 

要让测试通过,您必须告诉Moq设置属性以具有标准属性行为。您可以通过调用SetupProperty来执行此操作,并且模拟看起来更像

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

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

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

答案 1 :(得分:4)

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