需要帮助更好地了解Moq

时间:2022-02-10 20:18:05

I've been looking at the Moq documentation and the comments are too short for me to understand each of things it can do.

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

The first thing I don't get is It.IsAny<string>(). //example using string

我没有得到的第一件事是It.IsAny ()。 //使用字符串的示例

Is there an advantage of using this over just putting some value in? I know people say to use this if you don't care about the value, but if you don't care about the value can't you just do "a" or something? This just seems like more typing.

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

Secondly, when would be an example be of when you would not care about the value? I thought Moq needs the value to match up stuff.

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

I don't get what It.Is<> is for at all or how to use it. I don't understand the example and what it is trying to show.

我不知道It.Is <>是为了什么或如何使用它。我不明白这个例子以及它试图展示的内容。

Next, I don't get when to use Times (and its AtMost methods and similar). Why would you limit the number of times something is set up? I have some AppConfig value that I need to use twice. Why would I want to limit it to, say, once? This would just make the test fail. Is this to stop other people from adding another one to your code or something?

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

I don't get how to use mock.SetupAllProperties(); What does it set up the properties with?

我不知道如何使用mock.SetupAllProperties();它用什么设置属性?

I don't also get why there are so many different ways to set up a property and what their differences are. The documentation has:

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

SetupGet(of property)
SetupGet<TProperty>

I noticed that a lot of the stuff in Moq shows () and <> - what's the difference between them and what would they look like in use?

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

I also don't get why they have SetupGet. Would you not use SetupSet to set a property? SetupSet has five different ways to use it in the documentation. Plus another one called SetupProperty. So I don't understand why there are so many.

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

On a side note, I am wondering if variables used in lambdas are independent of other lambdas. E.g.:

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

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

Would this be ok or would there be some conflict between the variable m?

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

Finally, I was watching this video and I am wondering if it shows Visual Studio. His Intellisense looks different. A lightbulb pops up for him (I am happy mine does not, as it brings back painful memories of netbeans), and there are lines going from one opening brace to the closing brace and etc.

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

Thanks :)

2 个解决方案

#1


It.IsAny / It.Is

These can be useful when you're passing a new reference type within the code under test. For instance if you had a method along the lines of:-

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

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

You might want to check the add method has been called on the repository

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

If you wanted to make this test more explicit you can use It.Is by supplying a predicate the person object must match

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

This way the test will through an exception if the person object that was used to call the add method didn't have the age property set to 12.

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

Times

If you had a method along the lines of:-

如果你有一个方法: -

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

One of the things that you might want to test is that the pay method does not get called when a person aged over 65 is passed into the method

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

Similarly it's possible to imagine situations where you're iterating over a collection and calling a method for each item in the collection and you'd like to make sure that it's been called a certain amount of times, other times you simply don't care.

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

SetupGet / SetupSet

What you need to be aware of with these guys is that they reflect how your code is interacting with the mock rather than how you're setting up the mock

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

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

In this case the code is setting the ModifiedBy property of the IAuditable instance while it's getting the Name property of the current instance of IPrincipal

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

In this case we're setting up the name property on the mock of IPrincipal so it returns "test" when the getter is called on the Name property of Identity we're not setting the property itself.

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

SetupProperty / SetupAllProperties

Looking at the test above if it was changed to read

看看上面的测试是否改为阅读

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

The test would fail. This is because the proxy created by Moq doesn't actually do anything in the set method of a property unless you tell it to. In affect the mock object looks a bit like this

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

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

} 

To get the test to pass you have to tell Moq to setup the property to have the standard property behaviour. You can do this by calling SetupProperty and the mock will look more like

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

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

and the test above would pass as the value "test" would now get stored against the mock. When mocking complex objects you might want to do this for all properties, hence the SetupAllProperties shortcut

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

Finally, the lightbulb in the IDE is the resharper plugin.

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

#2


If you don't care about the exact value of a property, it's far better to use .IsAny because you are being explicit about the fact that the exact value is not important. If you hardcode it as "abc", then it is not clear if your code you are testing depends on starting with "a" or ending with "c" or being 3 chars long, etc. etc.

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

#1


It.IsAny / It.Is

These can be useful when you're passing a new reference type within the code under test. For instance if you had a method along the lines of:-

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

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

You might want to check the add method has been called on the repository

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

If you wanted to make this test more explicit you can use It.Is by supplying a predicate the person object must match

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

This way the test will through an exception if the person object that was used to call the add method didn't have the age property set to 12.

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

Times

If you had a method along the lines of:-

如果你有一个方法: -

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

One of the things that you might want to test is that the pay method does not get called when a person aged over 65 is passed into the method

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

Similarly it's possible to imagine situations where you're iterating over a collection and calling a method for each item in the collection and you'd like to make sure that it's been called a certain amount of times, other times you simply don't care.

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

SetupGet / SetupSet

What you need to be aware of with these guys is that they reflect how your code is interacting with the mock rather than how you're setting up the mock

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

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

In this case the code is setting the ModifiedBy property of the IAuditable instance while it's getting the Name property of the current instance of IPrincipal

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

In this case we're setting up the name property on the mock of IPrincipal so it returns "test" when the getter is called on the Name property of Identity we're not setting the property itself.

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

SetupProperty / SetupAllProperties

Looking at the test above if it was changed to read

看看上面的测试是否改为阅读

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

The test would fail. This is because the proxy created by Moq doesn't actually do anything in the set method of a property unless you tell it to. In affect the mock object looks a bit like this

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

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

} 

To get the test to pass you have to tell Moq to setup the property to have the standard property behaviour. You can do this by calling SetupProperty and the mock will look more like

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

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

and the test above would pass as the value "test" would now get stored against the mock. When mocking complex objects you might want to do this for all properties, hence the SetupAllProperties shortcut

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

Finally, the lightbulb in the IDE is the resharper plugin.

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

#2


If you don't care about the exact value of a property, it's far better to use .IsAny because you are being explicit about the fact that the exact value is not important. If you hardcode it as "abc", then it is not clear if your code you are testing depends on starting with "a" or ending with "c" or being 3 chars long, etc. etc.

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