单元测试:Mock数据仅首次返回有效值,后续返回null的问题求助
单元测试SolidEdge API时Mock数据仅首次调用有效问题解决
问题描述
我正在对与SolidEdge API(一款类似AutoCAD的COM组件,用于实现3D模型修改自动化)交互的方法进行单元测试。当前遇到的问题是:Mock数据仅能返回一次,后续调用会返回null。所有Mock配置在首次调用时正常工作,但第二次调用同一属性时就会失效。例如,从includeIn1中获取到值“Yes”,但从includeIn2中得到的却是null,尽管两者访问的是同一个属性。
相关代码
public static ResultModel GetMockedAssemblyDocument(MockRequestModel mockRequestModel) { var mockAssemblyDocument = new Mock<AssemblyDocument>(); var mockOccurrences = new Mock<Occurrences>(); var mockOccurrenceList = new List<Mock<Occurrence>>(); foreach (var occurrence in mockRequestModel.OccurrencesDetails) { var mockOccurrence = MockSolidEdgeDocumentOccurrence(occurrence.OccurrenceName, occurrence.Properties, occurrence.Variables); mockOccurrenceList.Add(mockOccurrence); } mockOccurrences.Setup(o => o.GetEnumerator()).Returns(mockOccurrenceList.Select(mock => mock.Object).GetEnumerator()); mockAssemblyDocument.Setup(doc => doc.Occurrences).Returns(mockOccurrences.Object); return new ResultModel { MockedAssemblyDocument = mockAssemblyDocument, MockedAssemblyDocumentCount = 2 }; } public static Mock<Occurrence> MockSolidEdgeDocumentOccurrence(string occurrenceName, Dictionary<string, object> properties, Dictionary<string, VariableInfo> variables) { // Existing code mockOccurrence1.SetupGet(o => o.OccurrenceDocument).Returns(() => { var mockSolidEdgeDocument = new Mock<SolidEdgeDocument>(); var mockPropertySets = GetPropertySetsMock(properties); mockSolidEdgeDocument.Setup(doc => doc.Properties).Returns(mockPropertySets.Object); return mockSolidEdgeDocument.Object; }); return mockOccurrence1; } private static Mock<PropertySets> GetPropertySetsMock(Dictionary<string, object> properties) { var mockPropertySets = new Mock<PropertySets>(); var mockPropertySet = new Mock<Properties>(); var propertyObjectType = new List<dynamic>(); foreach (var property in properties) { dynamic dynamicProperty = new ExpandoObject(); dynamicProperty.Name = property.Key; dynamicProperty.Value = property.Value; propertyObjectType.Add(dynamicProperty); } mockPropertySet.Setup(ps => ps.GetEnumerator()).Returns(propertyObjectType.GetEnumerator()); mockPropertySets.Setup(ps => ps.GetEnumerator()).Returns(new List<Properties> { mockPropertySet.Object }.GetEnumerator()); return mockPropertySets; } [Test] public void GetSupportData_ShouldReturnListOfComponents() { // Arrange var mockRequestModel = new MockRequestModel { OccurrencesDetails = new List<MockOccurrenceRequestModel> { new() { OccurrenceName = "Occurrence1", Properties = new Dictionary<string, object>() { { nameof(FilePropertyNameConstants.ObjectType), nameof(ObjectTypeConstants.Elbow) }, { nameof(FilePropertyNameConstants.IncludeIn), "Yes" } } } } }; var mockAssemblyDocument = GetMockedAssemblyDocument(mockRequestModel); _mockConnectionRepository.Setup(repo => repo.GetAssemblyDocument()).Returns(mockAssemblyDocument.MockedAssemblyDocument.Object); // Act var result = _isometricRepository.GetData(); // Assert Assert.That(result, Is.Not.Null); } public List<ResultModel> GetData() { // Existing code SolidEdgeDocument occurrenceDocument = occurrence.OccurrenceDocument; if (!occurrenceDocument.PropertyValueExists(FilePropertyNameConstants.IncludeIn, out object? includeIn1)) continue; if (!occurrenceDocument.PropertyValueExists(FilePropertyNameConstants.IncludeIn, out object? includeIn2)) continue; // Existing code }
问题原因
核心问题在于枚举器只能被遍历一次:
- 在
GetPropertySetsMock方法中,直接返回了预先创建好的枚举器实例。当第一次调用PropertyValueExists时,枚举器已经被遍历到末尾,第二次调用时没有数据可以返回,导致获取属性值失败。 - 同理,
mockOccurrences的GetEnumerator设置也存在同样问题,多次遍历Occurrences会出现失效情况。
解决方案
修改所有GetEnumerator的Setup逻辑,使用延迟创建枚举器的方式,每次调用GetEnumerator都返回一个新的实例:
1. 修改GetPropertySetsMock方法
private static Mock<PropertySets> GetPropertySetsMock(Dictionary<string, object> properties) { var mockPropertySets = new Mock<PropertySets>(); var mockPropertySet = new Mock<Properties>(); var propertyObjectType = new List<dynamic>(); foreach (var property in properties) { dynamic dynamicProperty = new ExpandoObject(); dynamicProperty.Name = property.Key; dynamicProperty.Value = property.Value; propertyObjectType.Add(dynamicProperty); } // 延迟返回新的枚举器 mockPropertySet.Setup(ps => ps.GetEnumerator()).Returns(() => propertyObjectType.GetEnumerator()); // 同样延迟返回新的枚举器 mockPropertySets.Setup(ps => ps.GetEnumerator()).Returns(() => new List<Properties> { mockPropertySet.Object }.GetEnumerator()); return mockPropertySets; }
2. 修改GetMockedAssemblyDocument中Occurrences的枚举器设置
// 延迟返回新的枚举器 mockOccurrences.Setup(o => o.GetEnumerator()).Returns(() => mockOccurrenceList.Select(mock => mock.Object).GetEnumerator());
原理说明
使用Returns(() => ...)的Lambda表达式,每次调用GetEnumerator方法时都会执行Lambda逻辑,创建并返回一个全新的枚举器实例,这样每次遍历都能从头开始获取数据,避免了枚举器耗尽导致的返回null问题。
内容的提问来源于stack exchange,提问作者prisan
相关产品推荐
相关产品推荐

