在单元测试中访问具有相同类型对象的两个不同列表

Posted

技术标签:

【中文标题】在单元测试中访问具有相同类型对象的两个不同列表【英文标题】:Acess two different lists with the same type of objects in unit testing 【发布时间】:2021-11-23 07:46:12 【问题描述】:

如果我能解释我的问题,这是两个方法,一个测试方法和它的目标:

测试方法

[Fact]
public async Task Create_Post_Valid_Id_Valid_Name()

#region Arrange
    var model = new FixedShippingScheduleEditViewModelId = 10, Name = "Schedule";
    var schedules = new List<Hyper360FixedShippingScheduleSummaryModel>()
    new Hyper360FixedShippingScheduleSummaryModelId = 11, Name = "AnotherSchedule";
    var refreshedSchedules = new List<Hyper360FixedShippingScheduleSummaryModel>()
    new Hyper360FixedShippingScheduleSummaryModelId = 10, Name = "Schedule";
    _shippingApiClient.Setup(x => x.GetAllFixedSchedules(CancellationToken.None)).Returns(Task.FromResult((IList<Hyper360FixedShippingScheduleSummaryModel>)schedules));
    _shippingApiClient.Setup(x => x.CreateFixedSchedule(It.Is<FixedShippingScheduleEditModel>(s => s.Name == "Schedule"), CancellationToken.None)).Returns(Task.CompletedTask);
    _shippingApiClient.Setup(x => x.GetAllFixedSchedules(CancellationToken.None)).Returns(Task.FromResult((IList<Hyper360FixedShippingScheduleSummaryModel>)refreshedSchedules));
    _localizationService.Setup(x => x.GetResource("Bizay.Logistic.Admin.Schedules.Notification.Create.Error")).Returns("Success");
    _notificationService.Setup(x => x.SuccessNotification("Success", true));
#endregion
#region Act
    var result = await _scheduleController.Create(model, true, CancellationToken.None);
#endregion
#region Assert
    result.Should().BeOfType<ViewResult>();
    ((ViewResult)result).Model.Should().BeOfType<FixedShippingScheduleEditViewModel>();
    ((FixedShippingScheduleEditViewModel)((ViewResult)result).Model).Name.Should().Be("AnotherSchedule");
    ((FixedShippingScheduleEditViewModel)((ViewResult)result).Model).Id.Should().Be(11);
#endregion

测试方法

[HttpPost]
[ValidateAntiForgeryToken]
[HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")]
public async Task<ActionResult> Create(FixedShippingScheduleEditViewModel model, bool continueEditing, CancellationToken cancellationToken)

    var schedules = await _shippingApiClient.GetAllFixedSchedules(cancellationToken);
    if (schedules.Any(s => s.Name.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase)))
    
        ModelState.AddModelError(nameof(model.Name), _localizationService.GetResource("Bizay.Logistic.Admin.Schedules.Error.Name.Repeated"));
    
    if (ModelState.IsValid)
    
        await _shippingApiClient.CreateFixedSchedule(new FixedShippingScheduleEditModel  Name = model.Name , cancellationToken);

        var refreshedSchedules = await _shippingApiClient.GetAllFixedSchedules(cancellationToken);
        var schedule = refreshedSchedules?.FirstOrDefault(s => s.Name == model.Name);

        if (schedule == null)
        
            _notificationService.ErrorNotification(_localizationService.GetResource("Bizay.Logistic.Admin.Schedules.Notification.Create.Error"));
            return View(model);
        
        _notificationService.SuccessNotification(_localizationService.GetResource("Bizay.Logistic.Admin.Schedules.Notification.Create.Success"));
        if (continueEditing)
        
            return RedirectToAction("Edit", new  id = schedule.Id );
        
        else
        
            return RedirectToAction("List");
        
    
    return View(model);

这里发生的情况是 schedules.Any(s =&gt; s.Name.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase)) 应该在 schedules 列表上工作,而 refreshedSchedules?.FirstOrDefault(s =&gt; s.Name == model.Name) 应该在 refreshedSchedules 列表上工作,问题是两个 lambda 都在作用于 schedules清单,知道我该如何完成这项工作吗?

【问题讨论】:

您的minimal reproducible example 缺少ToIList 方法... 尝试在 GetAllFixedSchedules 的设置中使用 It.Is<...>(...)。 @AlexeiLevenkov public IList&lt;T&gt; ToIList&lt;T&gt;(List&lt;T&gt; t) return t; @AlexeiLevenkov 我用演员替换了ToIlist 我自己也去过那里。有时在我们寻求简化的过程中,它删除了太多的上下文。通过练习找到平衡。 【参考方案1】:

假设_shippingApiClient 是最小起订量Mock&lt;ShippingApiClient&gt;,您想设置一个Callback 填充一个列表,稍后调用GetAllFixedSchedules() 将完成:

//Arrnge
int id = 10;
string expectedName = "Schedule";
var viewModel = new FixedShippingScheduleEditViewModel  Name = expectedName ;
var scheduleList = new List<Hyper360FixedShippingScheduleSummaryModel>() 
 
    new Hyper360FixedShippingScheduleSummaryModel  Id = id++, Name = "AnotherSchedule"  //Id = 10
;

_shippingApiClient
    .Setup(x => x.CreateFixedSchedule(It.IsAny<FixedShippingScheduleEditModel>(), It.IsAny<CancellationToken>()))
    .Callback((FixedShippingScheduleEditModel model, CancellationToken token) => 
        // TODO: perhaps some asserts here
        model.Id = id; //Since id is being asserted
        scheduleList.Add(new Hyper360FixedShippingScheduleSummaryModel
        
            Id = model.Id,
            Name = model.Name
        );
    )
    .Returns(Task.CompletedTask);

_shippingApiClient
    .Setup(x => x.GetAllFixedSchedules(It.IsAny<CancellationToken>()))
    .ReturnsAsync(() => scheduleList);

//...code omitted for brevity

//Act
var result = await _scheduleController.Create(viewModel, true, CancellationToken.None);


//Assert
result.Should().BeOfType<RedirectToActionResult>();
RedirectToActionResult redirect = (RedirectToActionResult) result;
redirect.ActionName.Should().Be("Edit");
redirect.RouteValues["id"].Should().Be(id);

通过回调,您可以设置具有某种(本地保存)状态的模拟。

或者,设置顺序调用,请参阅Different return values the first and second time with Moq,您首先返回初始列表,然后返回添加项目的列表,但这很脆弱 - 如果您在中间添加另一个调用,结果将关闭。

【讨论】:

以上是关于在单元测试中访问具有相同类型对象的两个不同列表的主要内容,如果未能解决你的问题,请参考以下文章

是否可以为具有 1)返回类型 void、2)访问说明符私有或受保护的方法编写单元测试?

如何比较相同类型的两个对象的值?

用相同的测试测试两个目标

UICollectionView 两个具有相同视图的单元格。第二个单元格不显示任何内容

使用 Auto Layout 创建具有多个不同自定义单元格的 UITableView 具有几乎相同的子视图

在 Perl 中测试两个哈希键是不是具有相同的结构