需要帮助才能更好地了解起订量

发布于 2024-07-26 12:50:37 字数 1358 浏览 2 评论 0原文

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

我不明白的第一件事是 It.IsAny()。 //使用字符串的示例

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

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

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

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

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

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

SetupGet(of property)
SetupGet<TProperty>

我注意到 Moq 中的很多内容都显示 ()<> - 它们之间有什么区别以及它们在使用中的样子?

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

顺便说一句,我想知道 lambda 中使用的变量是否独立于其他 lambda。 例如:

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

这样可以吗,或者变量 m 之间会存在冲突吗?

最后,我观看此视频,我想知道它是否显示 Visual Studio。 他的智能感知看起来有所不同。 一个灯泡为他弹出(我很高兴我的没有弹出,因为它带回了 netbean 的痛苦记忆),并且有从左大括号到右大括号的线条,等等。

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

The first thing I don't get is 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.

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.

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.

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?

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

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 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.

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

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

Would this be ok or would there be some conflict between the variable 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

幸福还没到 2024-08-02 12:50:38

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

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

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

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

看上面的测试如果改成 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);
}

测试就会失败。 这是因为 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 插件。

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,

[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,

[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.

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

[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,

[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.

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

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

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

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

乙白 2024-08-02 12:50:38

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文