简体   繁体   English

使用Moq模拟集合行为

[英]mocking collection behavior with Moq

I've read through some of the discussions on the Moq user group and have failed to find an example and have been so far unable to find the scenario that I have. 我已经阅读了一些关于Moq用户组的讨论并且未能找到示例,并且到目前为止还无法找到我所拥有的场景。 Here is my question and code: 这是我的问题和代码:

// 6 periods
var schedule = new List<PaymentPlanPeriod>()
{
    new PaymentPlanPeriod(1000m, args.MinDate.ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(1).ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(2).ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(3).ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(4).ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(5).ToString())
};

// Now the proxy is correct with the schedule
helper.Setup(h => h.GetPlanPeriods(It.IsAny<String>(), schedule));

Then in my tests I use Periods but the Mocked _PaymentPlanHelper never populates the collection, see below for usage: 然后在我的测试中我使用Periods但是Mocked _PaymentPlanHelper从不填充集合,请参阅下面的用法:

public IEnumerable<PaymentPlanPeriod> Periods
{
 get
 {
  if (CanCalculateExpression())
     _PaymentPlanHelper.GetPlanPeriods(this.ToString(), _PaymentSchedule);

  return _PaymentSchedule;
 }
}

Now if I change the mocked object to use another overloaded method of GetPlanPeriods that returns a List like so : 现在,如果我更改模拟对象以使用GetPlanPeriods的另一个重载方法返回List,如下所示:

var schedule = new List<PaymentPlanPeriod>()
{
    new PaymentPlanPeriod(1000m, args.MinDate.ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(1).ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(2).ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(3).ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(4).ToString()),
    new PaymentPlanPeriod(1000m, args.MinDate.Value.AddMonths(5).ToString())
};

helper.Setup(h => h.GetPlanPeriods(It.IsAny<String>())).Returns(new List<PaymentPlanPeriod>(schedule));

List<PaymentPlanPeriod> result = new _PaymentPlanHelper.GetPlanPeriods(this.ToString());

This works as expected. 这按预期工作。 Any pointers would be awesome, as long as you don't bash my architecture... :) 任何指针都会很棒,只要你不打击我的架构...... :)

Thank you, Stephen 谢谢你,斯蒂芬

The Setup-Method is basically to setup some expeced behavior. Setup-Method基本上是设置一些已经过的行为。 For example to setup a Mock-Instance to return certain values. 例如,设置Mock-Instance以返回某些值。 For example a configuration-mock: 例如,配置模拟:

var configMock = new Mock<IConfiguration>();
configMock.Setup(c=>c.GetSetting("Title")).Returns("Hello Word");
configMock.Setup(c=>c.GetSetting("Answer")).Returns("42");

This means, than when you pass "Title" to the GetSetting-Method the mock will return "Hello Word". 这意味着,当您将“Title”传递给GetSetting-Method时,mock将返回“Hello Word”。 And when you pass "Answer" to the mock, it will return "42". 当你将“回答”传递给模拟器时,它将返回“42”。 Addionally you can configure wildcards. 另外,您可以配置通配符。 For example: 例如:

var configMock = new Mock<IConfiguration>();
configMock.Setup(c=>c.GetSetting(It.IsAny<String>())).Returns("Hello Word");

Now the mock will return for any call to the GetSetting-Method the string "Hello World" 现在mock将返回对GetSetting-Method的任何调用字符串“Hello World”

Now in your first case you set up the mock in this way: Expect a call to GetPlanPeriods with any string and this list. 现在,在第一种情况下,您以这种方式设置模拟:期望使用任何字符串和此列表调用GetPlanPeriods。 So with the second parameter you specify which list you expect. 因此,使用第二个参数指定您期望的列表。 But you don't setup any bahvior for this call. 但是你没有为这个电话设置任何行为。

Afaik you can setup Moq to something with the arguments. Afaik你可以用参数设置Moq。 Like this: 像这样:

helper.Setup(c => c.GetPlanPeriods(It.IsAny<string>(),It.IsAny<List<PaymentPlanPeriod>>()))
            .Callback((string s, List<PaymentPlanPeriod> l)=>
                          {
                              l.Add(new PaymentPlanPeriod(1000m, args.MinDate.ToString()));
                          });

In the second example you set up the mock in this way: Expect a call to GetPlanPeriods with any string and return then this list. 在第二个示例中,您以这种方式设置模拟:期望使用任何字符串调用GetPlanPeriods并返回此列表。 So it will return for any call the given list. 因此它将返回给定列表的任何调用。 Thats why it works. 这就是它的工作原理。

I general I would recommend to design your API in such a way that you're second example. 我一般情况下我会建议你设计你的API,这是你的第二个例子。 Prefer to return the changed collection instead of updating the passed collection. 更喜欢返回已更改的集合,而不是更新传递的集合。 Using and mocking such methods is way easier. 使用和模拟这样的方法更容易。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM