需要帮助更好地了解Moq

cho*_*bo2 43 c# lambda moq mocking

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

我没有得到的第一件事是 It.IsAny<string>(). //example using string

使用它有什么优势而不仅仅是增加一些价值?我知道有人说如果你不关心这个价值就用这个,但如果你不关心价值,你不能只做"一个"或者什么吗?这似乎更像打字.

其次,什么时候你会不关心价值?我认为Moq需要价值来匹配东西.

我根本没有得到什么It.Is<>或如何使用它.我不明白这个例子以及它试图展示的内容.

接下来,我没有得到何时使用Times(及其AtMost方法和类似).为什么要限制设置的次数?我有一些AppConfig价值,我需要使用两次.为什么我要将它限制为,比如一次?这只会使测试失败.这是为了阻止其他人在你的代码或其他东西中添加另一个吗?

我没有得到如何使用mock.SetupAllProperties(); 它设置属性是什么?

我也不明白为什么有这么多不同的方式来建立一个房产以及它们之间的区别.该文件有:

SetupGet(of property)
SetupGet<TProperty>
Run Code Online (Sandbox Code Playgroud)

我注意到,很多在Moq的东西展示()<>-什么是他们,他们看起来像在使用中有什么区别?

我也不明白为什么他们有SetupGet.你不会SetupSet用来设置房产吗? SetupSet有五种不同的方法可以在文档中使用它.另外一个叫SetupProperty.所以我不明白为什么会这么多.

另外,我想知道lambdas中使用的变量是否独立于其他lambdas.例如:

mock.setup(m => m.Test);
stop.setup(m => m.Test);
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢 :)

Joh*_*ter 104

It.IsAny/It.Is

当您在被测代码中传递新的引用类型时,这些功能非常有用.例如,如果你有一个方法: -

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

您可能希望检查已在存储库上调用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>()));
}
Run Code Online (Sandbox Code Playgroud)

如果你想让这个测试更加明确,你可以通过提供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)));
}
Run Code Online (Sandbox Code Playgroud)

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

如果你有一个方法: -

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

您可能想要测试的一件事是,当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());
}
Run Code Online (Sandbox Code Playgroud)

类似地,可以想象你正在迭代一个集合并为集合中的每个项目调用一个方法的情况,你想确保它被调用了一定次数,有时候你根本就不在乎.

SetupGet/SetupSet

你需要注意的是这些人反映你的代码是如何与模拟交互的,而不是你如何设置模拟

public static void SetAuditProperties(IAuditable auditable) {
    auditable.ModifiedBy = Thread.CurrentPrincipal.Identity.Name;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,代码正在设置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");
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们在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);
}
Run Code Online (Sandbox Code Playgroud)

测试会失败.这是因为Moq创建的代理实际上并没有在属性的set方法中做任何事情,除非你告诉它.在影响模拟对象看起来有点像这样

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

} 
Run Code Online (Sandbox Code Playgroud)

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

public class AuditableMock : IAuditable {
     public string ModifiedBy { get; set; }
} 
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这是一个测试,它过度指定了它试图描述的代码的行为,但它也是一个测试,它演示了It.IsAny而不是简洁,这是原始问题的内容. (4认同)
  • 康拉德,像这样添加测试的重点是接受回归.如果有人进入并修改CreatePerson方法并更改其实现,以便"Add"不会显式调用,则需要了解它.因此,通过测试确认方法被调用,没有歧义.请记住,说它"正确在代码中"与大型复杂的代码库无关.所有的东西都在代码中 - 测试是为了确保那里有什么*应该*存在. (4认同)

And*_*hop 5

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