在单元测试中访问具有相同类型对象的两个不同列表
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 => s.Name.Equals(model.Name, StringComparison.InvariantCultureIgnoreCase))
应该在 schedules
列表上工作,而 refreshedSchedules?.FirstOrDefault(s => s.Name == model.Name)
应该在 refreshedSchedules
列表上工作,问题是两个 lambda 都在作用于 schedules
清单,知道我该如何完成这项工作吗?
【问题讨论】:
您的minimal reproducible example 缺少ToIList
方法...
尝试在 GetAllFixedSchedules 的设置中使用 It.Is<...>(...)。
@AlexeiLevenkov public IList<T> ToIList<T>(List<T> t) return t;
@AlexeiLevenkov 我用演员替换了ToIlist
。
我自己也去过那里。有时在我们寻求简化的过程中,它删除了太多的上下文。通过练习找到平衡。
【参考方案1】:
假设_shippingApiClient
是最小起订量Mock<ShippingApiClient>
,您想设置一个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 两个具有相同视图的单元格。第二个单元格不显示任何内容