A:common基础功能及测试

This commit is contained in:
jimmy committed 2025-11-14 10:22:34 +08:00
1 parent 9ffa70acde
commit 237cbd8788
11 files changed
+5929 -32

No files matched your search

+674
View File
@@ -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
}