【问题标题】:Using moq to setup a method to return a list of objects but getting null使用 moq 设置方法以返回对象列表但获取 null
【发布时间】:2016-07-20 06:05:01
【问题描述】:

我一直在我的个人项目中进行测试并遇到了这个小问题。我有一个创建对象列表的测试方法,我设置了一个在我测试的方法中使用的服务以返回模拟列表。但是,由于某种原因,设置无法正常工作并且返回 null。

这里是测试方法:

var mockList = new List<IBillItem>
{
    new BillItem
    {
        Id = 0,
        DueDate = new DateTime(),
        Name = "",
        IndexNumber = "",
        AccountNumber = "",
        Amount = decimal.One
    },
    new BillItem
    {
        Id = 0,
        DueDate = new DateTime(),
        Name = "",
        IndexNumber = "",
        AccountNumber = "",
        Amount = decimal.One
    }
};

_billHandlingService.Setup(x => x.GetAllBillsAsync(It.IsAny<string>())).Returns(Task.FromResult(mockList));

var listBillsVm = new ListBillsViewModel(new LoggerFactory(), _billHandlingService.Object, _settingsService.Object);

await listBillsVm.GetBillsAsync();

_billHandlingService.Verify(x => x.GetAllBillsAsync(_settingsService.Name), Times.AtMostOnce);

Assert.AreEqual(1, listBillsVm.BillsList.Count);

这里是具体类im测试的代码:

public async Task GetBillsAsync()
{
    BillsList.Clear();

    var bills = await _billHandlingService.GetAllBillsAsync(_settingsService.LoggerUser);

    if (null != bills)
    {
        var billsByDate = bills.Where(x => x.DueDate == DateTime.Today).ToList();
        foreach (var bill in billsByDate)
        {
            BillsList.Add(bill);
            RaisePropertyChanged(nameof(BillsList));
        }
    }
}

我已尝试在 SO / google 上搜索结果,但尚未找到任何答案。提前致谢。

编辑:代码没有注释,但我相信它足够清楚,在 cmets 中询问是否有需要清除的东西

编辑2:

Task<List<IBillItem>>GetAllBillsAsync(string username); 

是被调用方法的接口。

【问题讨论】:

  • 尝试将Setup中的.Returns更改为.ReturnsAsync(mockList)
  • 我刚刚使用您的示例重新创建了一个单元测试,并且一切正常。您使用的是什么版本的起订量?
  • 起订量版本为 4.5.16。
  • 我没有降到 4.5.0 并且仍然是同样的问题。这真的很奇怪。
  • 我用 4.5.10.0 进行了测试,它工作正常。

标签: c# unit-testing async-await moq


【解决方案1】:

您可以尝试将Setup 中的.Returns 更改为.ReturnsAsync(mockList)

//...other code removed for brevity
_billHandlingService
    .Setup(x => x.GetAllBillsAsync(It.IsAny<string>()))
    .ReturnsAsync(mockList);
//...other code removed for brevity

更新

根据您的问题使用了以下最小的完整可验证示例来尝试重现您的问题。请注意,我省略了创建测试所不需要的任何类。

class ListBillsViewModel {
    private IBillHandlingService _billHandlingService;
    private ISettingsService _settingsService;

    public ListBillsViewModel(IBillHandlingService billHandlingService, ISettingsService settingsService) {
        this._billHandlingService = billHandlingService;
        this._settingsService = settingsService;
        BillsList = new List<IBillItem>();
    }

    public List<IBillItem> BillsList { get; set; }

    public async Task GetBillsAsync() {
        BillsList.Clear();

        var bills = await _billHandlingService.GetAllBillsAsync(_settingsService.LoggerUserName);

        if (null != bills) {
            var billsByDate = bills.Where(x => x.DueDate == DateTime.Today).ToList();
            foreach (var bill in billsByDate) {
                BillsList.Add(bill);
            }
        }
    }
}

public interface ISettingsService {
    string Name { get; }
    string LoggerUserName { get; set; }
}

public interface IBillHandlingService {
    Task<List<IBillItem>> GetAllBillsAsync(string username);
}

public class BillItem : IBillItem {
    public int Id { get; set; }
    public DateTime DueDate { get; set; }
    public string Name { get; set; }
    public string IndexNumber { get; set; }
    public string AccountNumber { get; set; }
    public decimal Amount { get; set; }
}

public interface IBillItem {
    int Id { get; set; }
    DateTime DueDate { get; set; }
    string Name { get; set; }
    string IndexNumber { get; set; }
    string AccountNumber { get; set; }
    decimal Amount { get; set; }
}

然后基于上述类重构以下单元测试

[TestMethod]
public async Task Moq_Setup_Should_Return_List_Of_Objects() {
    var mockList = new List<IBillItem>
    {
        new BillItem
        {
            Id = 0,
            DueDate = DateTime.Today,
            Name = "User",
            IndexNumber = "",
            AccountNumber = "",
            Amount = decimal.One
        },
        new BillItem
        {
            Id = 1,
            DueDate = DateTime.Today.AddDays(1),
            Name = "User",
            IndexNumber = "",
            AccountNumber = "",
            Amount = decimal.One
        }
    };

    string name = "User";

    var _settingsService = new Mock<ISettingsService>();
    _settingsService
        .Setup(m => m.Name)
        .Returns(name);
    _settingsService
        .Setup(m => m.LoggerUserName)
        .Returns(name);

    var _billHandlingService = new Mock<IBillHandlingService>();
    _billHandlingService
        .Setup(x => x.GetAllBillsAsync(It.IsAny<string>()))
        .ReturnsAsync(mockList);

    var listBillsVm = new ListBillsViewModel(_billHandlingService.Object, _settingsService.Object);

    await listBillsVm.GetBillsAsync();

    _billHandlingService.Verify(x => x.GetAllBillsAsync(_settingsService.Name), Times.AtMostOnce);

    Assert.AreEqual(1, listBillsVm.BillsList.Count);
}

我运行了上面的测试,对于 .Returns(Task.FromResult(mockist)).ReturnsAsync(mockList) 的两种设置,它都按预期通过了。

您提供的示例与您的实际情况不符,或者问题超出了您在帖子中描述的范围。

【讨论】:

  • 不工作。忘了提到我确实尝试在:/之前设置它 ReturnAsync。不管怎么说,多谢拉。还有什么想法吗?
  • @tjugg 我建议你再看看你的问题,因为根据你提供的内容它是不可重现的。
  • 这里会更新。通过将起订量从 4.5.16 降级到 4.5.0 解决了我遇到的问题。我会接受这个作为答案,因为您提供了一个完整的工作示例供其他人查看。
【解决方案2】:

您需要在Task.FromResult 上指定List&lt;IBillItem&gt;,如下所示:

_billHandlingService.Setup<Task<List<IBillItem>>>(
    x => x.GetAllBillsAsync(It.IsAny<string>()))
                    .Returns(Task.FromResult<List<IBillItem>>(mockList));

类似的 SO Q 和 A here.

【讨论】:

  • 仍然没有返回列表。获得空值。我不相信指定类型会做任何事情。不管怎么说,多谢拉。还有什么想法吗?
猜你喜欢
  • 2016-06-26
  • 2020-08-13
  • 1970-01-01
  • 2015-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多