C# 需要帮助才能更好地理解起订量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1073846/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:36:11  来源:igfitidea点击:

Need help to understand Moq better

c#mockinglambdamoq

提问by chobo2

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<string>(). //example using string

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.

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

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 AtMostmethods and similar). Why would you limit the number of times something is set up? I have some AppConfigvalue 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?

我注意到,在起订量显示了很多的东西()<>-什么是它们之间的区别,他们是什么样子的使用?

I also don't get why they have SetupGet. Would you not use SetupSetto set a property? SetupSethas 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.:

附带说明一下,我想知道 lambda 中使用的变量是否独立于其他 lambda。例如:

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 videoand 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。他的智能感知看起来不一样。一个灯泡为他弹出(我很高兴我的没有,因为它带回了对 netbeans 的痛苦回忆),并且从一个开括号到闭括号等等都有线。

采纳答案by John Foster

It.IsAny / It.Is

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,

如果你想让这个测试更明确,你可以使用 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)));
}

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

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 effect, 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 set up the property to have the standard property behavior. 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 插件。

回答by Andy_Vulhop

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 个字符等。