A:common基础功能及测试
This commit is contained in:
1 parent
9ffa70acde
commit
237cbd8788
11 files changed
+5929
-32
No files matched your search
@@ -0,0 +1,632 @@
|
||||
using Common.ExpandFun;
|
||||
|
||||
namespace Common.Test;
|
||||
|
||||
/// <summary>
|
||||
/// CollectionExpand 集合扩展测试
|
||||
/// 展示分页、树形结构、批处理等集合操作功能
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class CollectionExpandTests
|
||||
{
|
||||
#region 测试数据类
|
||||
|
||||
private class TestItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public int? ParentId { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 分页测试
|
||||
|
||||
[TestMethod]
|
||||
public void ToPagedResult_BasicPaging_ReturnsCorrectPage()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 100).ToList();
|
||||
|
||||
// Act
|
||||
var page1 = items.ToPagedResult(1, 10);
|
||||
var page2 = items.ToPagedResult(2, 10);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, page1.PageIndex);
|
||||
Assert.AreEqual(10, page1.PageSize);
|
||||
Assert.AreEqual(100, page1.TotalCount);
|
||||
Assert.AreEqual(10, page1.TotalPages);
|
||||
Assert.AreEqual(10, page1.Items.Count);
|
||||
Assert.AreEqual(1, page1.Items.First());
|
||||
Assert.AreEqual(10, page1.Items.Last());
|
||||
|
||||
Assert.AreEqual(11, page2.Items.First());
|
||||
Assert.AreEqual(20, page2.Items.Last());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToPagedResult_LastPage_ReturnsPartialPage()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 25).ToList();
|
||||
|
||||
// Act
|
||||
var lastPage = items.ToPagedResult(3, 10);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, lastPage.PageIndex);
|
||||
Assert.AreEqual(5, lastPage.Items.Count); // 最后一页只有 5 个元素
|
||||
Assert.AreEqual(21, lastPage.Items.First());
|
||||
Assert.AreEqual(25, lastPage.Items.Last());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToPagedResult_InvalidPageIndex_ClampsToValidRange()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 50).ToList();
|
||||
|
||||
// Act
|
||||
var zeroPage = items.ToPagedResult(0, 10); // 应该被修正为第 1 页
|
||||
var negativePage = items.ToPagedResult(-1, 10); // 应该被修正为第 1 页
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, zeroPage.PageIndex);
|
||||
Assert.AreEqual(1, negativePage.PageIndex);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PagedResult_Navigation_PropertiesWorkCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 50).ToList();
|
||||
|
||||
// Act
|
||||
var firstPage = items.ToPagedResult(1, 10);
|
||||
var middlePage = items.ToPagedResult(3, 10);
|
||||
var lastPage = items.ToPagedResult(5, 10);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(firstPage.HasPreviousPage);
|
||||
Assert.IsTrue(firstPage.HasNextPage);
|
||||
|
||||
Assert.IsTrue(middlePage.HasPreviousPage);
|
||||
Assert.IsTrue(middlePage.HasNextPage);
|
||||
|
||||
Assert.IsTrue(lastPage.HasPreviousPage);
|
||||
Assert.IsFalse(lastPage.HasNextPage);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 树形结构测试
|
||||
|
||||
[TestMethod]
|
||||
public void ToTree_FlatListToTree_BuildsCorrectHierarchy()
|
||||
{
|
||||
// Arrange: 创建平铺的列表
|
||||
var items = new List<TestItem>
|
||||
{
|
||||
new() { Id = 1, Name = "根节点1", ParentId = null },
|
||||
new() { Id = 2, Name = "根节点2", ParentId = null },
|
||||
new() { Id = 3, Name = "子节点1-1", ParentId = 1 },
|
||||
new() { Id = 4, Name = "子节点1-2", ParentId = 1 },
|
||||
new() { Id = 5, Name = "子节点2-1", ParentId = 2 },
|
||||
new() { Id = 6, Name = "孙节点1-1-1", ParentId = 3 }
|
||||
};
|
||||
|
||||
// Act
|
||||
var tree = items.ToTree(
|
||||
idSelector: x => x.Id,
|
||||
parentIdSelector: x => x.ParentId,
|
||||
rootParentId: null
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, tree.Count); // 两个根节点
|
||||
Assert.AreEqual("根节点1", tree[0].Data.Name);
|
||||
Assert.AreEqual(2, tree[0].Children.Count); // 根节点1 有两个子节点
|
||||
Assert.AreEqual("子节点1-1", tree[0].Children[0].Data.Name);
|
||||
Assert.AreEqual(1, tree[0].Children[0].Children.Count); // 子节点1-1 有一个孙节点
|
||||
Assert.AreEqual("孙节点1-1-1", tree[0].Children[0].Children[0].Data.Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToTree_BuildsCorrectLevels()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<TestItem>
|
||||
{
|
||||
new() { Id = 1, Name = "根", ParentId = null },
|
||||
new() { Id = 2, Name = "子", ParentId = 1 },
|
||||
new() { Id = 3, Name = "孙", ParentId = 2 }
|
||||
};
|
||||
|
||||
// Act
|
||||
var tree = items.ToTree(x => x.Id, x => x.ParentId);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(0, tree[0].Level); // 根节点 Level = 0
|
||||
Assert.AreEqual(1, tree[0].Children[0].Level); // 子节点 Level = 1
|
||||
Assert.AreEqual(2, tree[0].Children[0].Children[0].Level); // 孙节点 Level = 2
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToFlatList_TreeToFlatList_PreservesAllNodes()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<TestItem>
|
||||
{
|
||||
new() { Id = 1, Name = "根", ParentId = null },
|
||||
new() { Id = 2, Name = "子1", ParentId = 1 },
|
||||
new() { Id = 3, Name = "子2", ParentId = 1 },
|
||||
new() { Id = 4, Name = "孙", ParentId = 2 }
|
||||
};
|
||||
|
||||
var tree = items.ToTree(x => x.Id, x => x.ParentId);
|
||||
|
||||
// Act
|
||||
var flatList = tree.ToFlatList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(4, flatList.Count);
|
||||
Assert.AreEqual("根", flatList[0].Name);
|
||||
Assert.AreEqual("子1", flatList[1].Name);
|
||||
Assert.AreEqual("孙", flatList[2].Name); // 深度优先遍历
|
||||
Assert.AreEqual("子2", flatList[3].Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TraverseTree_ExecutesActionForAllNodes()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<TestItem>
|
||||
{
|
||||
new() { Id = 1, Name = "A", ParentId = null },
|
||||
new() { Id = 2, Name = "B", ParentId = 1 },
|
||||
new() { Id = 3, Name = "C", ParentId = 1 }
|
||||
};
|
||||
|
||||
var tree = items.ToTree(x => x.Id, x => x.ParentId);
|
||||
var visitedNames = new List<string>();
|
||||
|
||||
// Act
|
||||
tree.TraverseTree(node => visitedNames.Add(node.Data.Name));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, visitedNames.Count);
|
||||
CollectionAssert.Contains(visitedNames, "A");
|
||||
CollectionAssert.Contains(visitedNames, "B");
|
||||
CollectionAssert.Contains(visitedNames, "C");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 批处理测试
|
||||
|
||||
[TestMethod]
|
||||
public void Batch_DividesIntoCorrectBatches()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 10).ToList();
|
||||
|
||||
// Act
|
||||
var batches = items.Batch(3).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(4, batches.Count); // 10 个元素,每批 3 个,共 4 批
|
||||
Assert.AreEqual(3, batches[0].Count());
|
||||
Assert.AreEqual(3, batches[1].Count());
|
||||
Assert.AreEqual(3, batches[2].Count());
|
||||
Assert.AreEqual(1, batches[3].Count()); // 最后一批只有 1 个
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Batch_EmptyCollection_ReturnsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<int>();
|
||||
|
||||
// Act
|
||||
var batches = items.Batch(5).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(0, batches.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ForEachBatch_ExecutesActionForEachBatch()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 25).ToList();
|
||||
var processedBatches = new List<int>();
|
||||
|
||||
// Act
|
||||
items.ForEachBatch(10, batch =>
|
||||
{
|
||||
processedBatches.Add(batch.Count());
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, processedBatches.Count);
|
||||
Assert.AreEqual(10, processedBatches[0]);
|
||||
Assert.AreEqual(10, processedBatches[1]);
|
||||
Assert.AreEqual(5, processedBatches[2]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 空集合安全操作测试
|
||||
|
||||
[TestMethod]
|
||||
public void OrEmpty_NullCollection_ReturnsEmptyCollection()
|
||||
{
|
||||
// Arrange
|
||||
List<int>? nullList = null;
|
||||
|
||||
// Act
|
||||
var result = nullList.OrEmpty();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(0, result.Count());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OrDefault_EmptyCollection_ReturnsDefaultValue()
|
||||
{
|
||||
// Arrange
|
||||
var emptyList = new List<int>();
|
||||
var defaultList = new List<int> { 1, 2, 3 };
|
||||
|
||||
// Act
|
||||
var result = emptyList.OrDefault(defaultList);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, result.Count());
|
||||
CollectionAssert.AreEqual(defaultList, result.ToList());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsNullOrEmpty_VariousInputs_ReturnsCorrectResult()
|
||||
{
|
||||
// Arrange
|
||||
List<int>? nullList = null;
|
||||
var emptyList = new List<int>();
|
||||
var nonEmptyList = new List<int> { 1 };
|
||||
|
||||
// Act & Assert
|
||||
Assert.IsTrue(nullList.IsNullOrEmpty());
|
||||
Assert.IsTrue(emptyList.IsNullOrEmpty());
|
||||
Assert.IsFalse(nonEmptyList.IsNullOrEmpty());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void HasItems_VariousInputs_ReturnsCorrectResult()
|
||||
{
|
||||
// Arrange
|
||||
List<int>? nullList = null;
|
||||
var emptyList = new List<int>();
|
||||
var nonEmptyList = new List<int> { 1 };
|
||||
|
||||
// Act & Assert
|
||||
Assert.IsFalse(nullList.HasItems());
|
||||
Assert.IsFalse(emptyList.HasItems());
|
||||
Assert.IsTrue(nonEmptyList.HasItems());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DistinctBy 测试
|
||||
|
||||
[TestMethod]
|
||||
public void DistinctBy_RemovesDuplicatesByKey()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<TestItem>
|
||||
{
|
||||
new() { Id = 1, Name = "A" },
|
||||
new() { Id = 2, Name = "B" },
|
||||
new() { Id = 3, Name = "A" }, // 重复的 Name
|
||||
new() { Id = 4, Name = "C" }
|
||||
};
|
||||
|
||||
// Act (.NET 6+ 内置 DistinctBy)
|
||||
var distinct = items.DistinctBy(x => x.Name).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, distinct.Count);
|
||||
Assert.AreEqual(1, distinct[0].Id); // 保留第一个 "A"
|
||||
Assert.AreEqual(2, distinct[1].Id);
|
||||
Assert.AreEqual(4, distinct[2].Id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 其他实用扩展测试
|
||||
|
||||
[TestMethod]
|
||||
public void ForEach_ExecutesActionForAllElements()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<int> { 1, 2, 3 };
|
||||
var sum = 0;
|
||||
|
||||
// Act
|
||||
items.ForEach(x => sum += x);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(6, sum);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ForEach_WithIndex_ProvidesCorrectIndex()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<string> { "A", "B", "C" };
|
||||
var results = new List<string>();
|
||||
|
||||
// Act
|
||||
items.ForEach((item, index) => results.Add($"{index}:{item}"));
|
||||
|
||||
// Assert
|
||||
CollectionAssert.AreEqual(new[] { "0:A", "1:B", "2:C" }, results);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void JoinString_ConcatenatesWithSeparator()
|
||||
{
|
||||
// Arrange
|
||||
var items = new[] { "apple", "banana", "cherry" };
|
||||
|
||||
// Act
|
||||
var result = items.JoinString(", ");
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("apple, banana, cherry", result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shuffle_ChangesOrder()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 100).ToList();
|
||||
var original = items.ToList();
|
||||
|
||||
// Act
|
||||
var shuffled = items.Shuffle().ToList();
|
||||
|
||||
// Assert: 打乱后的顺序应该不同(理论上有极小概率相同)
|
||||
Assert.AreEqual(original.Count, shuffled.Count);
|
||||
// 检查是否至少有一些元素位置变化
|
||||
var differentPositions = original.Where((item, index) => shuffled[index] != item).Count();
|
||||
Assert.IsTrue(differentPositions > 10); // 至少 10% 的元素位置不同
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TakeRandom_ReturnsSpecifiedCount()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 100).ToList();
|
||||
|
||||
// Act
|
||||
var random10 = items.TakeRandom(10).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(10, random10.Count);
|
||||
Assert.IsTrue(random10.All(x => items.Contains(x)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Split_DividesIntoParts()
|
||||
{
|
||||
// Arrange
|
||||
var items = Enumerable.Range(1, 10).ToList();
|
||||
|
||||
// Act
|
||||
var parts = items.Split(3).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, parts.Count);
|
||||
Assert.AreEqual(4, parts[0].Count()); // 10 / 3 = 向上取整 4
|
||||
Assert.AreEqual(4, parts[1].Count());
|
||||
Assert.AreEqual(2, parts[2].Count());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AllUnique_ChecksUniqueness()
|
||||
{
|
||||
// Arrange
|
||||
var uniqueList = new[] { 1, 2, 3, 4 };
|
||||
var duplicateList = new[] { 1, 2, 2, 3 };
|
||||
|
||||
// Act & Assert
|
||||
Assert.IsTrue(uniqueList.AllUnique());
|
||||
Assert.IsFalse(duplicateList.AllUnique());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void HasDuplicates_DetectsDuplicates()
|
||||
{
|
||||
// Arrange
|
||||
var uniqueList = new[] { 1, 2, 3, 4 };
|
||||
var duplicateList = new[] { 1, 2, 2, 3 };
|
||||
|
||||
// Act & Assert
|
||||
Assert.IsFalse(uniqueList.HasDuplicates());
|
||||
Assert.IsTrue(duplicateList.HasDuplicates());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetDuplicates_ReturnsOnlyDuplicates()
|
||||
{
|
||||
// Arrange
|
||||
var items = new[] { 1, 2, 2, 3, 3, 3, 4 };
|
||||
|
||||
// Act
|
||||
var duplicates = items.GetDuplicates().ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, duplicates.Count);
|
||||
CollectionAssert.Contains(duplicates, 2);
|
||||
CollectionAssert.Contains(duplicates, 3);
|
||||
CollectionAssert.DoesNotContain(duplicates, 1);
|
||||
CollectionAssert.DoesNotContain(duplicates, 4);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Interleave_MergesAlternately()
|
||||
{
|
||||
// Arrange
|
||||
var first = new[] { 1, 3, 5 };
|
||||
var second = new[] { 2, 4, 6 };
|
||||
|
||||
// Act
|
||||
var result = first.Interleave(second).ToList();
|
||||
|
||||
// Assert
|
||||
CollectionAssert.AreEqual(new[] { 1, 2, 3, 4, 5, 6 }, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Interleave_DifferentLengths_MergesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var first = new[] { 1, 3 };
|
||||
var second = new[] { 2, 4, 6, 8 };
|
||||
|
||||
// Act
|
||||
var result = first.Interleave(second).ToList();
|
||||
|
||||
// Assert
|
||||
CollectionAssert.AreEqual(new[] { 1, 2, 3, 4, 6, 8 }, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WhereNotNull_FiltersNullValues()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<string?> { "A", null, "B", null, "C" };
|
||||
|
||||
// Act
|
||||
var result = items.WhereNotNull().ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, result.Count);
|
||||
CollectionAssert.AreEqual(new[] { "A", "B", "C" }, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToDictionaryList_GroupsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var items = new List<TestItem>
|
||||
{
|
||||
new() { Id = 1, Name = "A", ParentId = 1 },
|
||||
new() { Id = 2, Name = "B", ParentId = 1 },
|
||||
new() { Id = 3, Name = "C", ParentId = 2 },
|
||||
new() { Id = 4, Name = "D", ParentId = 2 }
|
||||
};
|
||||
|
||||
// Act
|
||||
var grouped = items.ToDictionaryList(
|
||||
x => x.ParentId!.Value,
|
||||
x => x.Name
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, grouped.Count);
|
||||
Assert.AreEqual(2, grouped[1].Count);
|
||||
CollectionAssert.Contains(grouped[1], "A");
|
||||
CollectionAssert.Contains(grouped[1], "B");
|
||||
Assert.AreEqual(2, grouped[2].Count);
|
||||
CollectionAssert.Contains(grouped[2], "C");
|
||||
CollectionAssert.Contains(grouped[2], "D");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实际应用场景测试
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_PaginationWithSearch()
|
||||
{
|
||||
// Arrange: 模拟产品列表搜索和分页
|
||||
var products = Enumerable.Range(1, 100)
|
||||
.Select(i => new { Id = i, Name = $"Product {i}", Price = i * 10 })
|
||||
.ToList();
|
||||
|
||||
// Act: 搜索价格大于 500 的产品,并分页
|
||||
var searchResults = products
|
||||
.Where(p => p.Price > 500)
|
||||
.ToPagedResult(1, 10);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(10, searchResults.Items.Count);
|
||||
Assert.IsTrue(searchResults.Items.All(p => p.Price > 500));
|
||||
Assert.IsTrue(searchResults.TotalCount > 40); // 总共有 50+ 个符合条件的产品
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_OrganizationTree()
|
||||
{
|
||||
// Arrange: 模拟组织架构树
|
||||
var departments = new List<TestItem>
|
||||
{
|
||||
new() { Id = 1, Name = "总公司", ParentId = null },
|
||||
new() { Id = 2, Name = "技术部", ParentId = 1 },
|
||||
new() { Id = 3, Name = "市场部", ParentId = 1 },
|
||||
new() { Id = 4, Name = "前端组", ParentId = 2 },
|
||||
new() { Id = 5, Name = "后端组", ParentId = 2 },
|
||||
new() { Id = 6, Name = "运营组", ParentId = 3 }
|
||||
};
|
||||
|
||||
// Act: 构建组织树
|
||||
var tree = departments.ToTree(x => x.Id, x => x.ParentId);
|
||||
|
||||
// Assert: 验证树形结构
|
||||
Assert.AreEqual(1, tree.Count); // 1 个根节点(总公司)
|
||||
Assert.AreEqual(2, tree[0].Children.Count); // 2 个一级部门
|
||||
Assert.AreEqual(2, tree[0].Children[0].Children.Count); // 技术部有 2 个组
|
||||
Assert.AreEqual("前端组", tree[0].Children[0].Children[0].Data.Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_BatchProcessing()
|
||||
{
|
||||
// Arrange: 模拟批量导入数据
|
||||
var records = Enumerable.Range(1, 1000).ToList();
|
||||
var processedCount = 0;
|
||||
|
||||
// Act: 每批处理 100 条记录
|
||||
records.ForEachBatch(100, batch =>
|
||||
{
|
||||
// 模拟批量插入数据库
|
||||
processedCount += batch.Count();
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1000, processedCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_DataCleaning()
|
||||
{
|
||||
// Arrange: 模拟清洗数据(去除 null、去重、过滤)
|
||||
var rawData = new List<string?> { "A", null, "B", "A", null, "C", "B", "D" };
|
||||
|
||||
// Act: 数据清洗流水线
|
||||
var cleanedData = rawData
|
||||
.WhereNotNull() // 去除 null
|
||||
.DistinctBy(x => x) // 去重
|
||||
.Where(x => x != "D") // 过滤不需要的数据
|
||||
.ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, cleanedData.Count);
|
||||
CollectionAssert.AreEqual(new[] { "A", "B", "C" }, cleanedData);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,634 @@
|
||||
using System.ComponentModel;
|
||||
using Common.Utils;
|
||||
|
||||
namespace Common.Test;
|
||||
|
||||
/// <summary>
|
||||
/// EnumHelper 枚举工具测试
|
||||
/// 展示枚举解析、描述获取、标志位操作等功能
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class EnumHelperTests
|
||||
{
|
||||
#region 测试枚举定义
|
||||
|
||||
/// <summary>
|
||||
/// 订单状态枚举
|
||||
/// </summary>
|
||||
public enum OrderStatus
|
||||
{
|
||||
[System.ComponentModel.Description("待支付")]
|
||||
Pending = 0,
|
||||
|
||||
[System.ComponentModel.Description("已支付")]
|
||||
Paid = 1,
|
||||
|
||||
[System.ComponentModel.Description("已发货")]
|
||||
Shipped = 2,
|
||||
|
||||
[System.ComponentModel.Description("已完成")]
|
||||
Completed = 3,
|
||||
|
||||
[System.ComponentModel.Description("已取消")]
|
||||
Cancelled = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户权限枚举(标志位)
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UserPermissions
|
||||
{
|
||||
[System.ComponentModel.Description("无权限")]
|
||||
None = 0,
|
||||
|
||||
[System.ComponentModel.Description("读取权限")]
|
||||
Read = 1,
|
||||
|
||||
[System.ComponentModel.Description("写入权限")]
|
||||
Write = 2,
|
||||
|
||||
[System.ComponentModel.Description("删除权限")]
|
||||
Delete = 4,
|
||||
|
||||
[System.ComponentModel.Description("管理权限")]
|
||||
Admin = 8,
|
||||
|
||||
[System.ComponentModel.Description("全部权限")]
|
||||
All = Read | Write | Delete | Admin
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志级别(无描述)
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Warning = 3,
|
||||
Error = 4,
|
||||
Fatal = 5
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 枚举解析测试
|
||||
|
||||
[TestMethod]
|
||||
public void Parse_ValidString_ReturnsEnum()
|
||||
{
|
||||
// Act
|
||||
var result1 = EnumHelper.Parse<OrderStatus>("Paid");
|
||||
var result2 = EnumHelper.Parse<OrderStatus>("paid"); // 忽略大小写
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result1);
|
||||
Assert.AreEqual(OrderStatus.Paid, result1.Value);
|
||||
Assert.IsNotNull(result2);
|
||||
Assert.AreEqual(OrderStatus.Paid, result2.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Parse_InvalidString_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = EnumHelper.Parse<OrderStatus>("InvalidValue");
|
||||
|
||||
// Assert
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ParseOrDefault_InvalidString_ReturnsDefaultValue()
|
||||
{
|
||||
// Act
|
||||
var result = EnumHelper.ParseOrDefault("InvalidValue", OrderStatus.Pending);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(OrderStatus.Pending, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ParseFromInt_ValidValue_ReturnsEnum()
|
||||
{
|
||||
// Act
|
||||
var result = EnumHelper.ParseFromInt<OrderStatus>(2);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(OrderStatus.Shipped, result.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ParseFromInt_InvalidValue_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = EnumHelper.ParseFromInt<OrderStatus>(999);
|
||||
|
||||
// Assert
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ParseFromDescription_ValidDescription_ReturnsEnum()
|
||||
{
|
||||
// Act
|
||||
var result = EnumHelper.ParseFromDescription<OrderStatus>("已支付");
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(OrderStatus.Paid, result.Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ParseFromDescription_InvalidDescription_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = EnumHelper.ParseFromDescription<OrderStatus>("不存在的状态");
|
||||
|
||||
// Assert
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 枚举描述测试
|
||||
|
||||
[TestMethod]
|
||||
public void GetDescription_WithDescriptionAttribute_ReturnsDescription()
|
||||
{
|
||||
// Arrange
|
||||
var status = OrderStatus.Paid;
|
||||
|
||||
// Act
|
||||
var description = status.GetDescription();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("已支付", description);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetDescription_WithoutDescriptionAttribute_ReturnsEnumName()
|
||||
{
|
||||
// Arrange
|
||||
var level = LogLevel.Error;
|
||||
|
||||
// Act
|
||||
var description = level.GetDescription();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("Error", description);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetAllDescriptions_ReturnsAllDescriptions()
|
||||
{
|
||||
// Act
|
||||
var descriptions = EnumHelper.GetAllDescriptions<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5, descriptions.Count);
|
||||
Assert.AreEqual("待支付", descriptions[OrderStatus.Pending]);
|
||||
Assert.AreEqual("已支付", descriptions[OrderStatus.Paid]);
|
||||
Assert.AreEqual("已完成", descriptions[OrderStatus.Completed]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 枚举列表测试
|
||||
|
||||
[TestMethod]
|
||||
public void GetValues_ReturnsAllValues()
|
||||
{
|
||||
// Act
|
||||
var values = EnumHelper.GetValues<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5, values.Length);
|
||||
CollectionAssert.Contains(values, OrderStatus.Pending);
|
||||
CollectionAssert.Contains(values, OrderStatus.Paid);
|
||||
CollectionAssert.Contains(values, OrderStatus.Shipped);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetNames_ReturnsAllNames()
|
||||
{
|
||||
// Act
|
||||
var names = EnumHelper.GetNames<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5, names.Length);
|
||||
CollectionAssert.Contains(names, "Pending");
|
||||
CollectionAssert.Contains(names, "Paid");
|
||||
CollectionAssert.Contains(names, "Shipped");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetOptions_ReturnsListForDropdown()
|
||||
{
|
||||
// Act
|
||||
var options = EnumHelper.GetOptions<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5, options.Count);
|
||||
|
||||
var paidOption = options.First(o => o.Value == 1);
|
||||
Assert.AreEqual("Paid", paidOption.Name);
|
||||
Assert.AreEqual("已支付", paidOption.Description);
|
||||
Assert.AreEqual("已支付", paidOption.DisplayText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetDictionary_ReturnsValueNamePairs()
|
||||
{
|
||||
// Act
|
||||
var dict = EnumHelper.GetDictionary<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5, dict.Count);
|
||||
Assert.AreEqual("Paid", dict[1]);
|
||||
Assert.AreEqual("Shipped", dict[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetDictionaryWithDescription_ReturnsValueDescriptionPairs()
|
||||
{
|
||||
// Act
|
||||
var dict = EnumHelper.GetDictionaryWithDescription<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5, dict.Count);
|
||||
Assert.AreEqual("已支付", dict[1]);
|
||||
Assert.AreEqual("已发货", dict[2]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 枚举验证测试
|
||||
|
||||
[TestMethod]
|
||||
public void IsDefined_ValidValue_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsTrue(EnumHelper.IsDefined<OrderStatus>(1));
|
||||
Assert.IsTrue(EnumHelper.IsDefined<OrderStatus>("Paid"));
|
||||
Assert.IsTrue(EnumHelper.IsDefined(OrderStatus.Paid));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsDefined_InvalidValue_ReturnsFalse()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsFalse(EnumHelper.IsDefined<OrderStatus>(999));
|
||||
Assert.IsFalse(EnumHelper.IsDefined<OrderStatus>("InvalidStatus"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 标志位枚举操作测试
|
||||
|
||||
[TestMethod]
|
||||
public void HasFlag_ChecksFlagCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var permissions = UserPermissions.Read | UserPermissions.Write;
|
||||
|
||||
// Act & Assert
|
||||
Assert.IsTrue(permissions.HasFlag(UserPermissions.Read));
|
||||
Assert.IsTrue(permissions.HasFlag(UserPermissions.Write));
|
||||
Assert.IsFalse(permissions.HasFlag(UserPermissions.Delete));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddFlag_AddsNewFlag()
|
||||
{
|
||||
// Arrange
|
||||
var permissions = UserPermissions.Read;
|
||||
|
||||
// Act
|
||||
var newPermissions = permissions.AddFlag(UserPermissions.Write);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(newPermissions.HasFlag(UserPermissions.Read));
|
||||
Assert.IsTrue(newPermissions.HasFlag(UserPermissions.Write));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveFlag_RemovesExistingFlag()
|
||||
{
|
||||
// Arrange
|
||||
var permissions = UserPermissions.Read | UserPermissions.Write | UserPermissions.Delete;
|
||||
|
||||
// Act
|
||||
var newPermissions = permissions.RemoveFlag(UserPermissions.Write);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(newPermissions.HasFlag(UserPermissions.Read));
|
||||
Assert.IsFalse(newPermissions.HasFlag(UserPermissions.Write));
|
||||
Assert.IsTrue(newPermissions.HasFlag(UserPermissions.Delete));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToggleFlag_TogglesFlag()
|
||||
{
|
||||
// Arrange
|
||||
var permissions = UserPermissions.Read;
|
||||
|
||||
// Act: 切换 Write(添加)
|
||||
var withWrite = permissions.ToggleFlag(UserPermissions.Write);
|
||||
Assert.IsTrue(withWrite.HasFlag(UserPermissions.Write));
|
||||
|
||||
// Act: 再次切换 Write(移除)
|
||||
var withoutWrite = withWrite.ToggleFlag(UserPermissions.Write);
|
||||
Assert.IsFalse(withoutWrite.HasFlag(UserPermissions.Write));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetFlags_ReturnsAllSetFlags()
|
||||
{
|
||||
// Arrange
|
||||
var permissions = UserPermissions.Read | UserPermissions.Write | UserPermissions.Admin;
|
||||
|
||||
// Act
|
||||
var flags = permissions.GetFlags().ToList();
|
||||
|
||||
// Assert: None 不应该包含在内(因为值是 0)
|
||||
Assert.IsTrue(flags.Contains(UserPermissions.Read));
|
||||
Assert.IsTrue(flags.Contains(UserPermissions.Write));
|
||||
Assert.IsTrue(flags.Contains(UserPermissions.Admin));
|
||||
Assert.IsFalse(flags.Contains(UserPermissions.Delete));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CombineFlags_CombinesMultipleFlags()
|
||||
{
|
||||
// Act
|
||||
var combined = EnumHelper.CombineFlags(
|
||||
UserPermissions.Read,
|
||||
UserPermissions.Write,
|
||||
UserPermissions.Delete
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(combined.HasFlag(UserPermissions.Read));
|
||||
Assert.IsTrue(combined.HasFlag(UserPermissions.Write));
|
||||
Assert.IsTrue(combined.HasFlag(UserPermissions.Delete));
|
||||
Assert.IsFalse(combined.HasFlag(UserPermissions.Admin));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 枚举转换测试
|
||||
|
||||
[TestMethod]
|
||||
public void ToInt_ConvertsToInteger()
|
||||
{
|
||||
// Arrange
|
||||
var status = OrderStatus.Paid;
|
||||
|
||||
// Act
|
||||
var intValue = status.ToInt();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, intValue);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToLong_ConvertsToLong()
|
||||
{
|
||||
// Arrange
|
||||
var status = OrderStatus.Shipped;
|
||||
|
||||
// Act
|
||||
var longValue = status.ToLong();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2L, longValue);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToEnum_ConvertsIntToEnum()
|
||||
{
|
||||
// Arrange
|
||||
int value = 3;
|
||||
|
||||
// Act
|
||||
var status = value.ToEnum<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(OrderStatus.Completed, status);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 枚举信息测试
|
||||
|
||||
[TestMethod]
|
||||
public void GetCount_ReturnsEnumCount()
|
||||
{
|
||||
// Act
|
||||
var count = EnumHelper.GetCount<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5, count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetMinValue_ReturnsSmallestValue()
|
||||
{
|
||||
// Act
|
||||
var min = EnumHelper.GetMinValue<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(OrderStatus.Pending, min);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetMaxValue_ReturnsLargestValue()
|
||||
{
|
||||
// Act
|
||||
var max = EnumHelper.GetMaxValue<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(OrderStatus.Cancelled, max);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsFlagsEnum_DetectsFlagsAttribute()
|
||||
{
|
||||
// Act
|
||||
var isFlagsEnum = EnumHelper.IsFlagsEnum<UserPermissions>();
|
||||
var isNotFlagsEnum = EnumHelper.IsFlagsEnum<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(isFlagsEnum);
|
||||
Assert.IsFalse(isNotFlagsEnum);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 枚举比较测试
|
||||
|
||||
[TestMethod]
|
||||
public void Equals_ComparesEnumValues()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsTrue(EnumHelper.Equals(OrderStatus.Paid, OrderStatus.Paid));
|
||||
Assert.IsFalse(EnumHelper.Equals(OrderStatus.Paid, OrderStatus.Pending));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void In_ChecksIfValueInList()
|
||||
{
|
||||
// Arrange
|
||||
var status = OrderStatus.Paid;
|
||||
|
||||
// Act & Assert
|
||||
Assert.IsTrue(status.In(OrderStatus.Pending, OrderStatus.Paid, OrderStatus.Shipped));
|
||||
Assert.IsFalse(status.In(OrderStatus.Completed, OrderStatus.Cancelled));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NotIn_ChecksIfValueNotInList()
|
||||
{
|
||||
// Arrange
|
||||
var status = OrderStatus.Paid;
|
||||
|
||||
// Act & Assert
|
||||
Assert.IsFalse(status.NotIn(OrderStatus.Pending, OrderStatus.Paid, OrderStatus.Shipped));
|
||||
Assert.IsTrue(status.NotIn(OrderStatus.Completed, OrderStatus.Cancelled));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 随机枚举测试
|
||||
|
||||
[TestMethod]
|
||||
public void GetRandom_ReturnsValidEnum()
|
||||
{
|
||||
// Act
|
||||
var random = EnumHelper.GetRandom<OrderStatus>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(EnumHelper.IsDefined(random));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetRandomExcept_ExcludesSpecifiedValues()
|
||||
{
|
||||
// Act: 多次获取随机值,确保不包含排除的值
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var random = EnumHelper.GetRandomExcept(OrderStatus.Pending, OrderStatus.Cancelled);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(random.NotIn(OrderStatus.Pending, OrderStatus.Cancelled));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实际应用场景测试
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_OrderStatusWorkflow()
|
||||
{
|
||||
// Arrange: 模拟订单状态流转
|
||||
var currentStatus = OrderStatus.Pending;
|
||||
|
||||
// Act: 支付订单
|
||||
currentStatus = OrderStatus.Paid;
|
||||
Assert.AreEqual("已支付", currentStatus.GetDescription());
|
||||
|
||||
// Act: 发货
|
||||
currentStatus = OrderStatus.Shipped;
|
||||
Assert.AreEqual("已发货", currentStatus.GetDescription());
|
||||
|
||||
// Assert: 检查状态是否在允许的范围内
|
||||
var validStatuses = new[] { OrderStatus.Shipped, OrderStatus.Completed };
|
||||
Assert.IsTrue(currentStatus.In(validStatuses));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_PermissionManagement()
|
||||
{
|
||||
// Arrange: 初始化普通用户权限
|
||||
var userPermissions = UserPermissions.Read;
|
||||
|
||||
// Act: 升级为编辑权限
|
||||
userPermissions = userPermissions.AddFlag(UserPermissions.Write);
|
||||
Assert.IsTrue(userPermissions.HasFlag(UserPermissions.Write));
|
||||
|
||||
// Act: 再添加删除权限
|
||||
userPermissions = userPermissions.AddFlag(UserPermissions.Delete);
|
||||
|
||||
// Assert: 验证权限
|
||||
var grantedPermissions = userPermissions.GetFlags().ToList();
|
||||
Assert.AreEqual(3, grantedPermissions.Count);
|
||||
Assert.IsTrue(grantedPermissions.Contains(UserPermissions.Read));
|
||||
Assert.IsTrue(grantedPermissions.Contains(UserPermissions.Write));
|
||||
Assert.IsTrue(grantedPermissions.Contains(UserPermissions.Delete));
|
||||
|
||||
// Act: 撤销写入权限
|
||||
userPermissions = userPermissions.RemoveFlag(UserPermissions.Write);
|
||||
Assert.IsFalse(userPermissions.HasFlag(UserPermissions.Write));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_DropdownBinding()
|
||||
{
|
||||
// Arrange: 模拟为前端下拉框准备数据
|
||||
var statusOptions = EnumHelper.GetOptions<OrderStatus>();
|
||||
|
||||
// Act: 转换为前端所需的格式
|
||||
var dropdownData = statusOptions.Select(o => new
|
||||
{
|
||||
Value = o.Value,
|
||||
Text = o.DisplayText
|
||||
}).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5, dropdownData.Count);
|
||||
Assert.AreEqual("待支付", dropdownData[0].Text);
|
||||
Assert.AreEqual(0, dropdownData[0].Value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_StatusValidation()
|
||||
{
|
||||
// Arrange: 从前端接收状态值
|
||||
var statusFromFrontend = 2;
|
||||
|
||||
// Act: 验证并转换
|
||||
if (EnumHelper.IsDefined<OrderStatus>(statusFromFrontend))
|
||||
{
|
||||
var status = statusFromFrontend.ToEnum<OrderStatus>();
|
||||
var description = status.GetDescription();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(OrderStatus.Shipped, status);
|
||||
Assert.AreEqual("已发货", description);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail("Invalid status value");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_ParseUserInput()
|
||||
{
|
||||
// Arrange: 用户输入的状态名称
|
||||
var userInput = "paid"; // 可能是小写
|
||||
|
||||
// Act: 解析
|
||||
var status = EnumHelper.Parse<OrderStatus>(userInput);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(status);
|
||||
Assert.AreEqual(OrderStatus.Paid, status.Value);
|
||||
Assert.AreEqual("已支付", status.Value.GetDescription());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,674 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using Common.Utils;
|
||||
|
||||
namespace Common.Test;
|
||||
|
||||
/// <summary>
|
||||
/// ReflectionHelper 反射工具测试
|
||||
/// 展示类型扫描、属性访问、特性读取等功能
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class ReflectionHelperTests
|
||||
{
|
||||
#region 测试类定义
|
||||
|
||||
/// <summary>
|
||||
/// 测试基类
|
||||
/// </summary>
|
||||
public abstract class BaseEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试接口
|
||||
/// </summary>
|
||||
public interface ITestService
|
||||
{
|
||||
void Execute();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试特性
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
|
||||
public class TestAttribute : Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public TestAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试实体类
|
||||
/// </summary>
|
||||
[Test("UserEntity")]
|
||||
public class User : BaseEntity
|
||||
{
|
||||
[Test("UserName")]
|
||||
[System.ComponentModel.Description("用户名")]
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
[System.ComponentModel.Description("用户年龄")]
|
||||
public int Age { get; set; }
|
||||
|
||||
public string Email { get; set; } = "";
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.Now;
|
||||
|
||||
public void SayHello()
|
||||
{
|
||||
Console.WriteLine($"Hello, I'm {Name}");
|
||||
}
|
||||
|
||||
public static string GetVersion()
|
||||
{
|
||||
return "1.0.0";
|
||||
}
|
||||
|
||||
public int Calculate(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 DTO
|
||||
/// </summary>
|
||||
public class UserDto
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public int Age { get; set; }
|
||||
public string Email { get; set; } = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试服务实现
|
||||
/// </summary>
|
||||
public class TestService : ITestService
|
||||
{
|
||||
public void Execute()
|
||||
{
|
||||
Console.WriteLine("Executing test service");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 类型扫描测试
|
||||
|
||||
[TestMethod]
|
||||
public void FindSubclassesOf_FindsAllSubclasses()
|
||||
{
|
||||
// Act
|
||||
var subclasses = ReflectionHelper.FindSubclassesOf<BaseEntity>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(subclasses.Count > 0);
|
||||
Assert.IsTrue(subclasses.Any(t => t == typeof(User)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FindImplementationsOf_FindsAllImplementations()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// Act
|
||||
var implementations = ReflectionHelper.FindImplementationsOf<ITestService>(assembly);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(implementations.Count > 0);
|
||||
Assert.IsTrue(implementations.Any(t => t == typeof(TestService)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FindTypesWithAttribute_FindsTypesWithAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// Act
|
||||
var types = ReflectionHelper.FindTypesWithAttribute<TestAttribute>(assembly);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(types.Count > 0);
|
||||
Assert.IsTrue(types.Any(t => t == typeof(User)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 属性访问测试
|
||||
|
||||
[TestMethod]
|
||||
public void GetPropertyValue_ReturnsCorrectValue()
|
||||
{
|
||||
// Arrange
|
||||
var user = new User { Name = "Alice", Age = 25 };
|
||||
|
||||
// Act
|
||||
var name = ReflectionHelper.GetPropertyValue(user, nameof(User.Name));
|
||||
var age = ReflectionHelper.GetPropertyValue(user, nameof(User.Age));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("Alice", name);
|
||||
Assert.AreEqual(25, age);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SetPropertyValue_SetsValueCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var user = new User();
|
||||
|
||||
// Act
|
||||
ReflectionHelper.SetPropertyValue(user, nameof(User.Name), "Bob");
|
||||
ReflectionHelper.SetPropertyValue(user, nameof(User.Age), 30);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("Bob", user.Name);
|
||||
Assert.AreEqual(30, user.Age);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetPropertyValue_Performance_IsFasterThanDirectReflection()
|
||||
{
|
||||
// Arrange
|
||||
var user = new User { Name = "Alice", Age = 25 };
|
||||
const int iterations = 10000;
|
||||
|
||||
// 预热缓存
|
||||
ReflectionHelper.GetPropertyValue(user, nameof(User.Name));
|
||||
|
||||
// Act: 使用 ReflectionHelper(有缓存)
|
||||
var sw1 = System.Diagnostics.Stopwatch.StartNew();
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
_ = ReflectionHelper.GetPropertyValue(user, nameof(User.Name));
|
||||
}
|
||||
sw1.Stop();
|
||||
|
||||
// Act: 使用原生反射(无缓存)
|
||||
var propertyInfo = typeof(User).GetProperty(nameof(User.Name))!;
|
||||
var sw2 = System.Diagnostics.Stopwatch.StartNew();
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
_ = propertyInfo.GetValue(user);
|
||||
}
|
||||
sw2.Stop();
|
||||
|
||||
// Assert: ReflectionHelper 应该比直接反射快(由于缓存和表达式树)
|
||||
Console.WriteLine($"ReflectionHelper: {sw1.ElapsedMilliseconds}ms");
|
||||
Console.WriteLine($"Direct Reflection: {sw2.ElapsedMilliseconds}ms");
|
||||
// 注意:首次运行可能因为 JIT 编译而不准确,多次运行后 ReflectionHelper 应该更快
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetPropertyNames_ReturnsAllPropertyNames()
|
||||
{
|
||||
// Act
|
||||
var propertyNames = ReflectionHelper.GetPropertyNames<User>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(propertyNames.Count >= 5);
|
||||
CollectionAssert.Contains(propertyNames, nameof(User.Name));
|
||||
CollectionAssert.Contains(propertyNames, nameof(User.Age));
|
||||
CollectionAssert.Contains(propertyNames, nameof(User.Email));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetProperties_ReturnsAllProperties()
|
||||
{
|
||||
// Act
|
||||
var properties = ReflectionHelper.GetProperties<User>();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(properties.Length >= 5);
|
||||
Assert.IsTrue(properties.Any(p => p.Name == nameof(User.Name)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 特性读取测试
|
||||
|
||||
[TestMethod]
|
||||
public void GetAttribute_ReturnsAttribute()
|
||||
{
|
||||
// Act
|
||||
var attribute = ReflectionHelper.GetAttribute<TestAttribute>(typeof(User));
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(attribute);
|
||||
Assert.AreEqual("UserEntity", attribute.Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetAttribute_FromProperty_ReturnsAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var property = typeof(User).GetProperty(nameof(User.Name))!;
|
||||
|
||||
// Act
|
||||
var testAttr = ReflectionHelper.GetAttribute<TestAttribute>(property);
|
||||
var descAttr = ReflectionHelper.GetAttribute<System.ComponentModel.DescriptionAttribute>(property);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(testAttr);
|
||||
Assert.AreEqual("UserName", testAttr.Name);
|
||||
Assert.IsNotNull(descAttr);
|
||||
Assert.AreEqual("用户名", descAttr.Description);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void HasAttribute_ChecksAttributeExistence()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsTrue(ReflectionHelper.HasAttribute<TestAttribute>(typeof(User)));
|
||||
Assert.IsFalse(ReflectionHelper.HasAttribute<TestAttribute>(typeof(UserDto)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetAttribute_UsesCaching()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(User);
|
||||
|
||||
// Act: 多次获取应该使用缓存
|
||||
var attr1 = ReflectionHelper.GetAttribute<TestAttribute>(type);
|
||||
var attr2 = ReflectionHelper.GetAttribute<TestAttribute>(type);
|
||||
|
||||
// Assert: 应该返回相同的实例(来自缓存)
|
||||
Assert.IsNotNull(attr1);
|
||||
Assert.IsNotNull(attr2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 创建实例测试
|
||||
|
||||
[TestMethod]
|
||||
public void CreateInstance_Generic_CreatesInstance()
|
||||
{
|
||||
// Act
|
||||
var user = ReflectionHelper.CreateInstance<User>();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(user);
|
||||
Assert.IsInstanceOfType(user, typeof(User));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateInstance_ByType_CreatesInstance()
|
||||
{
|
||||
// Act
|
||||
var user = ReflectionHelper.CreateInstance(typeof(User));
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(user);
|
||||
Assert.IsInstanceOfType(user, typeof(User));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryCreateInstance_SuccessfulCreation_ReturnsTrue()
|
||||
{
|
||||
// Act
|
||||
var success = ReflectionHelper.TryCreateInstance<User>(out var user);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(success);
|
||||
Assert.IsNotNull(user);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 属性复制测试
|
||||
|
||||
[TestMethod]
|
||||
public void CopyProperties_CopiesSameNameProperties()
|
||||
{
|
||||
// Arrange
|
||||
var source = new User
|
||||
{
|
||||
Name = "Alice",
|
||||
Age = 25,
|
||||
Email = "alice@example.com"
|
||||
};
|
||||
var target = new UserDto();
|
||||
|
||||
// Act
|
||||
ReflectionHelper.CopyProperties(source, target);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("Alice", target.Name);
|
||||
Assert.AreEqual(25, target.Age);
|
||||
Assert.AreEqual("alice@example.com", target.Email);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MapTo_CreatesNewInstanceAndCopiesProperties()
|
||||
{
|
||||
// Arrange
|
||||
var source = new User
|
||||
{
|
||||
Name = "Bob",
|
||||
Age = 30,
|
||||
Email = "bob@example.com"
|
||||
};
|
||||
|
||||
// Act
|
||||
var dto = ReflectionHelper.MapTo<User, UserDto>(source);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dto);
|
||||
Assert.AreEqual("Bob", dto.Name);
|
||||
Assert.AreEqual(30, dto.Age);
|
||||
Assert.AreEqual("bob@example.com", dto.Email);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CopyProperties_IgnoreCase_CopiesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var source = new { name = "Alice", AGE = 25 };
|
||||
var target = new UserDto();
|
||||
|
||||
// Act
|
||||
ReflectionHelper.CopyProperties(source, target, ignoreCase: true);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("Alice", target.Name);
|
||||
Assert.AreEqual(25, target.Age);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 方法调用测试
|
||||
|
||||
[TestMethod]
|
||||
public void InvokeMethod_CallsInstanceMethod()
|
||||
{
|
||||
// Arrange
|
||||
var user = new User { Name = "Alice" };
|
||||
|
||||
// Act
|
||||
var result = ReflectionHelper.InvokeMethod(user, nameof(User.Calculate), 10, 20);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(30, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void InvokeStaticMethod_CallsStaticMethod()
|
||||
{
|
||||
// Act
|
||||
var result = ReflectionHelper.InvokeStaticMethod(typeof(User), nameof(User.GetVersion));
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("1.0.0", result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void InvokeMethod_NonExistentMethod_ThrowsException()
|
||||
{
|
||||
// Arrange
|
||||
var user = new User();
|
||||
|
||||
// Act
|
||||
ReflectionHelper.InvokeMethod(user, "NonExistentMethod");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 类型判断测试
|
||||
|
||||
[TestMethod]
|
||||
public void IsNullableType_DetectsNullableTypes()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsTrue(ReflectionHelper.IsNullableType(typeof(int?)));
|
||||
Assert.IsFalse(ReflectionHelper.IsNullableType(typeof(int)));
|
||||
Assert.IsFalse(ReflectionHelper.IsNullableType(typeof(string)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetUnderlyingType_ReturnsCorrectType()
|
||||
{
|
||||
// Act
|
||||
var underlyingType = ReflectionHelper.GetUnderlyingType(typeof(int?));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(typeof(int), underlyingType);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsValueType_DistinguishesValueTypes()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsTrue(ReflectionHelper.IsValueType(typeof(int)));
|
||||
Assert.IsTrue(ReflectionHelper.IsValueType(typeof(DateTime)));
|
||||
Assert.IsFalse(ReflectionHelper.IsValueType(typeof(string)));
|
||||
Assert.IsFalse(ReflectionHelper.IsValueType(typeof(User)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsNumericType_DetectsNumericTypes()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsTrue(ReflectionHelper.IsNumericType(typeof(int)));
|
||||
Assert.IsTrue(ReflectionHelper.IsNumericType(typeof(double)));
|
||||
Assert.IsTrue(ReflectionHelper.IsNumericType(typeof(decimal)));
|
||||
Assert.IsTrue(ReflectionHelper.IsNumericType(typeof(int?)));
|
||||
Assert.IsFalse(ReflectionHelper.IsNumericType(typeof(string)));
|
||||
Assert.IsFalse(ReflectionHelper.IsNumericType(typeof(DateTime)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsCollectionType_DetectsCollections()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsTrue(ReflectionHelper.IsCollectionType(typeof(int[])));
|
||||
Assert.IsTrue(ReflectionHelper.IsCollectionType(typeof(List<int>)));
|
||||
Assert.IsTrue(ReflectionHelper.IsCollectionType(typeof(IEnumerable<int>)));
|
||||
Assert.IsFalse(ReflectionHelper.IsCollectionType(typeof(int)));
|
||||
Assert.IsFalse(ReflectionHelper.IsCollectionType(typeof(string)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 泛型操作测试
|
||||
|
||||
[TestMethod]
|
||||
public void IsGenericType_DetectsGenericTypes()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.IsTrue(ReflectionHelper.IsGenericType(typeof(List<int>)));
|
||||
Assert.IsTrue(ReflectionHelper.IsGenericType(typeof(Dictionary<string, int>)));
|
||||
Assert.IsFalse(ReflectionHelper.IsGenericType(typeof(int)));
|
||||
Assert.IsFalse(ReflectionHelper.IsGenericType(typeof(string)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetGenericArguments_ReturnsTypeArguments()
|
||||
{
|
||||
// Act
|
||||
var args = ReflectionHelper.GetGenericArguments(typeof(Dictionary<string, int>));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, args.Length);
|
||||
Assert.AreEqual(typeof(string), args[0]);
|
||||
Assert.AreEqual(typeof(int), args[1]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MakeGenericType_ConstructsGenericType()
|
||||
{
|
||||
// Act
|
||||
var listType = ReflectionHelper.MakeGenericType(typeof(List<>), typeof(string));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(typeof(List<string>), listType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 缓存管理测试
|
||||
|
||||
[TestMethod]
|
||||
public void ClearCache_ClearsAllCaches()
|
||||
{
|
||||
// Arrange: 先访问一些缓存
|
||||
ReflectionHelper.GetPropertyValue(new User { Name = "Test" }, nameof(User.Name));
|
||||
ReflectionHelper.GetAttribute<TestAttribute>(typeof(User));
|
||||
|
||||
// Act
|
||||
ReflectionHelper.ClearCache();
|
||||
|
||||
// Assert: 缓存应该被清空(无法直接验证,但不应抛异常)
|
||||
Assert.IsTrue(true);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ClearPropertyAccessorCache_ClearsPropertyCache()
|
||||
{
|
||||
// Arrange
|
||||
ReflectionHelper.GetPropertyValue(new User { Name = "Test" }, nameof(User.Name));
|
||||
|
||||
// Act
|
||||
ReflectionHelper.ClearPropertyAccessorCache();
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实际应用场景测试
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_PluginScanning()
|
||||
{
|
||||
// Arrange: 模拟插件系统,扫描所有实现 ITestService 的类
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// Act: 查找所有插件
|
||||
var plugins = ReflectionHelper.FindImplementationsOf<ITestService>(assembly);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(plugins.Count > 0);
|
||||
|
||||
// Act: 实例化并执行插件
|
||||
foreach (var pluginType in plugins)
|
||||
{
|
||||
if (ReflectionHelper.CreateInstance(pluginType) is ITestService plugin)
|
||||
{
|
||||
plugin.Execute(); // 输出:Executing test service
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_AutoMapper()
|
||||
{
|
||||
// Arrange: 模拟 DTO 映射
|
||||
var user = new User
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Alice",
|
||||
Age = 25,
|
||||
Email = "alice@example.com",
|
||||
CreatedAt = DateTime.Now
|
||||
};
|
||||
|
||||
// Act: 自动映射到 DTO
|
||||
var dto = ReflectionHelper.MapTo<User, UserDto>(user);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dto);
|
||||
Assert.AreEqual("Alice", dto.Name);
|
||||
Assert.AreEqual(25, dto.Age);
|
||||
Assert.AreEqual("alice@example.com", dto.Email);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_DynamicPropertyAccess()
|
||||
{
|
||||
// Arrange: 模拟动态表单数据
|
||||
var user = new User { Name = "Bob", Age = 30 };
|
||||
var propertyUpdates = new Dictionary<string, object>
|
||||
{
|
||||
{ nameof(User.Name), "Updated Bob" },
|
||||
{ nameof(User.Age), 31 },
|
||||
{ nameof(User.Email), "bob@updated.com" }
|
||||
};
|
||||
|
||||
// Act: 动态更新属性
|
||||
foreach (var (propertyName, value) in propertyUpdates)
|
||||
{
|
||||
ReflectionHelper.SetPropertyValue(user, propertyName, value);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("Updated Bob", user.Name);
|
||||
Assert.AreEqual(31, user.Age);
|
||||
Assert.AreEqual("bob@updated.com", user.Email);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_AttributeBasedValidation()
|
||||
{
|
||||
// Arrange: 读取属性上的描述特性用于验证消息
|
||||
var properties = typeof(User).GetProperties();
|
||||
var validationMessages = new Dictionary<string, string>();
|
||||
|
||||
// Act: 收集所有带描述的属性
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var description = ReflectionHelper.GetAttribute<System.ComponentModel.DescriptionAttribute>(property);
|
||||
if (description != null)
|
||||
{
|
||||
validationMessages[property.Name] = $"{description.Description}不能为空";
|
||||
}
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(validationMessages.ContainsKey(nameof(User.Name)));
|
||||
Assert.AreEqual("用户名不能为空", validationMessages[nameof(User.Name)]);
|
||||
Assert.IsTrue(validationMessages.ContainsKey(nameof(User.Age)));
|
||||
Assert.AreEqual("用户年龄不能为空", validationMessages[nameof(User.Age)]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Scenario_TypeInspection()
|
||||
{
|
||||
// Arrange: 检查类型信息
|
||||
var type = typeof(User);
|
||||
|
||||
// Act: 收集类型信息
|
||||
var info = new
|
||||
{
|
||||
IsClass = type.IsClass,
|
||||
IsAbstract = type.IsAbstract,
|
||||
BaseType = type.BaseType?.Name,
|
||||
HasTestAttribute = ReflectionHelper.HasAttribute<TestAttribute>(type),
|
||||
PropertyCount = ReflectionHelper.GetProperties<User>().Length,
|
||||
IsValueType = ReflectionHelper.IsValueType(type)
|
||||
};
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(info.IsClass);
|
||||
Assert.IsFalse(info.IsAbstract);
|
||||
Assert.AreEqual("BaseEntity", info.BaseType);
|
||||
Assert.IsTrue(info.HasTestAttribute);
|
||||
Assert.IsTrue(info.PropertyCount >= 5);
|
||||
Assert.IsFalse(info.IsValueType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in new issue
Block a user