namespace Common.Test; /// /// Global 轻量级 DI 容器测试 /// 展示了依赖注入容器的所有功能和使用场景 /// [TestClass] public class GlobalTests { /// /// 每个测试方法执行前清除所有注册的服务,确保测试隔离 /// [TestInitialize] public void Setup() { Global.Clear(); } #region 基础注册和获取测试 /// /// 测试:注册并获取单例服务实例 /// 演示最基本的服务注册和获取 /// [TestMethod] public void RegisterSingleton_WithInstance_CanGetSameInstance() { // Arrange: 创建一个配置对象 var config = new AppConfig { AppName = "MyApp", Version = "1.0" }; // Act: 注册为单例 Global.RegisterSingleton(config); var retrieved = Global.Get(); // Assert: 应该返回同一个实例 Assert.AreSame(config, retrieved); Assert.AreEqual("MyApp", retrieved.AppName); } /// /// 测试:使用工厂方法注册单例 /// 演示延迟初始化,只有在第一次 Get 时才会调用工厂方法 /// [TestMethod] public void RegisterSingleton_WithFactory_CreatesInstanceOnFirstGet() { // Arrange: 创建一个计数器来跟踪工厂方法被调用的次数 var factoryCallCount = 0; // Act: 使用工厂方法注册 Global.RegisterSingleton(() => { factoryCallCount++; return new AppConfig { AppName = "FactoryApp" }; }); // Assert: 工厂方法还未被调用 Assert.AreEqual(0, factoryCallCount); // Act: 第一次获取 var first = Global.Get(); Assert.AreEqual(1, factoryCallCount); // 工厂方法被调用了一次 // Act: 第二次获取 var second = Global.Get(); Assert.AreEqual(1, factoryCallCount); // 工厂方法没有再次调用 Assert.AreSame(first, second); // 返回的是同一个实例 } /// /// 测试:注册瞬态服务,每次获取都创建新实例 /// [TestMethod] public void RegisterTransient_CreatesNewInstanceEachTime() { // Arrange & Act: 注册瞬态服务 Global.RegisterTransient(() => new RequestContext { RequestId = Guid.NewGuid().ToString(), Timestamp = DateTime.Now }); // Act: 多次获取 var first = Global.Get(); var second = Global.Get(); var third = Global.Get(); // Assert: 每次都是不同的实例 Assert.AreNotSame(first, second); Assert.AreNotSame(second, third); Assert.AreNotEqual(first.RequestId, second.RequestId); } /// /// 测试:使用通用 Register 方法指定生命周期 /// [TestMethod] public void Register_WithLifetime_CreatesCorrectLifetime() { // Arrange & Act: 使用通用方法注册单例 Global.Register(() => new CacheService(), ServiceLifetime.Singleton); var cache1 = Global.Get(); var cache2 = Global.Get(); // Assert: 单例应该是同一个实例 Assert.AreSame(cache1, cache2); // Arrange & Act: 使用通用方法注册瞬态 Global.Register(() => new OperationContext(), ServiceLifetime.Transient); var op1 = Global.Get(); var op2 = Global.Get(); // Assert: 瞬态应该是不同实例 Assert.AreNotSame(op1, op2); } #endregion #region 服务获取测试 /// /// 测试:获取未注册的服务会抛出异常 /// [TestMethod] public void Get_UnregisteredService_ThrowsException() { // Act & Assert: 尝试获取未注册的服务应该抛出异常 var exception = Assert.ThrowsException(() => { Global.Get(); }); Assert.IsTrue(exception.Message.Contains("ILogger")); } /// /// 测试:TryGet 安全地尝试获取服务 /// [TestMethod] public void TryGet_UnregisteredService_ReturnsFalse() { // Act: 尝试获取未注册的服务 var success = Global.TryGet(out var logger); // Assert: 应该返回 false 且 logger 为 null Assert.IsFalse(success); Assert.IsNull(logger); } /// /// 测试:TryGet 成功获取已注册的服务 /// [TestMethod] public void TryGet_RegisteredService_ReturnsTrue() { // Arrange: 注册服务 var expectedLogger = new ConsoleLogger(); Global.RegisterSingleton(expectedLogger); // Act: 尝试获取 var success = Global.TryGet(out var logger); // Assert: 应该成功获取 Assert.IsTrue(success); Assert.IsNotNull(logger); Assert.AreSame(expectedLogger, logger); } /// /// 测试:GetOrDefault 获取服务或返回默认值 /// [TestMethod] public void GetOrDefault_UnregisteredService_ReturnsDefault() { // Arrange: 创建默认值 var defaultLogger = new ConsoleLogger(); // Act: 获取未注册的服务 var logger = Global.GetOrDefault(defaultLogger); // Assert: 应该返回默认值 Assert.AreSame(defaultLogger, logger); } /// /// 测试:GetOrDefault 获取已注册的服务而不是默认值 /// [TestMethod] public void GetOrDefault_RegisteredService_ReturnsService() { // Arrange: 注册服务 var registeredLogger = new ConsoleLogger(); Global.RegisterSingleton(registeredLogger); var defaultLogger = new ConsoleLogger(); // Act: 获取服务 var logger = Global.GetOrDefault(defaultLogger); // Assert: 应该返回注册的服务,而不是默认值 Assert.AreSame(registeredLogger, logger); Assert.AreNotSame(defaultLogger, logger); } #endregion #region 服务更新和变更通知测试 /// /// 测试:更新单例服务实例 /// [TestMethod] public void Update_SingletonService_UpdatesInstance() { // Arrange: 注册初始配置 var oldConfig = new AppConfig { AppName = "OldApp", Version = "1.0" }; Global.RegisterSingleton(oldConfig); // Act: 更新配置 var newConfig = new AppConfig { AppName = "NewApp", Version = "2.0" }; Global.Update(newConfig); // Assert: 获取的应该是新配置 var retrieved = Global.Get(); Assert.AreSame(newConfig, retrieved); Assert.AreEqual("NewApp", retrieved.AppName); } /// /// 测试:更新瞬态服务会抛出异常 /// [TestMethod] public void Update_TransientService_ThrowsException() { // Arrange: 注册瞬态服务 Global.RegisterTransient(() => new RequestContext()); // Act & Assert: 尝试更新瞬态服务应该抛出异常 Assert.ThrowsException(() => { Global.Update(new RequestContext()); }); } /// /// 测试:订阅服务变更通知 /// 演示如何监听服务变更事件 /// [TestMethod] public void Subscribe_ServiceChange_ReceivesNotification() { // Arrange: 设置事件处理器 ServiceChangedEventArgs? receivedEventArgs = null; var callCount = 0; Global.Subscribe(e => { receivedEventArgs = e; callCount++; }); // Act: 注册初始服务 var oldConfig = new AppConfig { AppName = "OldApp", Version = "1.0" }; Global.RegisterSingleton(oldConfig); // 第一次注册不会触发通知(因为没有旧值) Assert.AreEqual(0, callCount); // Act: 更新服务 var newConfig = new AppConfig { AppName = "NewApp", Version = "2.0" }; Global.Update(newConfig); // Assert: 应该收到通知 Assert.AreEqual(1, callCount); Assert.IsNotNull(receivedEventArgs); Assert.AreSame(oldConfig, receivedEventArgs.OldValue); Assert.AreSame(newConfig, receivedEventArgs.NewValue); Assert.AreEqual("OldApp", receivedEventArgs.OldValue?.AppName); Assert.AreEqual("NewApp", receivedEventArgs.NewValue.AppName); } /// /// 测试:多个订阅者都能收到通知 /// [TestMethod] public void Subscribe_MultipleSubscribers_AllReceiveNotifications() { // Arrange: 创建多个订阅者 var subscriber1Called = false; var subscriber2Called = false; var subscriber3Called = false; Global.Subscribe(e => subscriber1Called = true); Global.Subscribe(e => subscriber2Called = true); Global.Subscribe(e => subscriber3Called = true); // Act: 注册并更新服务 Global.RegisterSingleton(new AppConfig { AppName = "App", Version = "1.0" }); Global.Update(new AppConfig { AppName = "App", Version = "2.0" }); // Assert: 所有订阅者都应该收到通知 Assert.IsTrue(subscriber1Called); Assert.IsTrue(subscriber2Called); Assert.IsTrue(subscriber3Called); } /// /// 测试:取消订阅后不再收到通知 /// [TestMethod] public void Unsubscribe_RemovesSubscriber() { // Arrange: 订阅通知 var callCount = 0; void Handler(ServiceChangedEventArgs e) { callCount++; } Global.Subscribe(Handler); Global.RegisterSingleton(new AppConfig { AppName = "App", Version = "1.0" }); // Act: 更新一次(应该收到通知) Global.Update(new AppConfig { AppName = "App", Version = "2.0" }); Assert.AreEqual(1, callCount); // Act: 取消订阅 Global.Unsubscribe(Handler); // Act: 再次更新(不应该收到通知) Global.Update(new AppConfig { AppName = "App", Version = "3.0" }); Assert.AreEqual(1, callCount); // 计数没有增加 } /// /// 测试:注册时启用 notifyIfExists 会触发通知 /// [TestMethod] public void RegisterSingleton_WithNotifyIfExists_TriggersNotification() { // Arrange: 订阅通知 var notificationCount = 0; Global.Subscribe(e => notificationCount++); // Act: 第一次注册 Global.RegisterSingleton(new AppConfig { AppName = "App", Version = "1.0" }); Assert.AreEqual(0, notificationCount); // 首次注册不触发 // Act: 使用 notifyIfExists = true 再次注册 Global.RegisterSingleton(new AppConfig { AppName = "App", Version = "2.0" }, true); // Assert: 应该触发通知 Assert.AreEqual(1, notificationCount); } #endregion #region 服务管理测试 /// /// 测试:检查服务是否已注册 /// [TestMethod] public void IsRegistered_ChecksServiceRegistration() { // Assert: 初始未注册 Assert.IsFalse(Global.IsRegistered()); // Act: 注册服务 Global.RegisterSingleton(new AppConfig()); // Assert: 现在已注册 Assert.IsTrue(Global.IsRegistered()); } /// /// 测试:注销服务 /// [TestMethod] public void Unregister_RemovesService() { // Arrange: 注册服务 Global.RegisterSingleton(new AppConfig { AppName = "TestApp" }); Assert.IsTrue(Global.IsRegistered()); // Act: 注销服务 var result = Global.Unregister(); // Assert: 服务已被移除 Assert.IsTrue(result); Assert.IsFalse(Global.IsRegistered()); // 尝试获取应该失败 Assert.ThrowsException(() => { Global.Get(); }); } /// /// 测试:清除所有服务 /// [TestMethod] public void Clear_RemovesAllServices() { // Arrange: 注册多个服务 Global.RegisterSingleton(new AppConfig()); Global.RegisterSingleton(new ConsoleLogger()); Global.RegisterTransient(() => new RequestContext()); Assert.AreEqual(3, Global.Count); // Act: 清除所有服务 Global.Clear(); // Assert: 所有服务都被移除 Assert.AreEqual(0, Global.Count); Assert.IsFalse(Global.IsRegistered()); Assert.IsFalse(Global.IsRegistered()); } /// /// 测试:获取已注册服务的数量 /// [TestMethod] public void Count_ReturnsCorrectNumber() { // Assert: 初始为 0 Assert.AreEqual(0, Global.Count); // Act: 注册服务 Global.RegisterSingleton(new AppConfig()); Assert.AreEqual(1, Global.Count); Global.RegisterSingleton(new ConsoleLogger()); Assert.AreEqual(2, Global.Count); Global.RegisterTransient(() => new RequestContext()); Assert.AreEqual(3, Global.Count); // Act: 注销一个服务 Global.Unregister(); Assert.AreEqual(2, Global.Count); } #endregion #region 线程安全测试 /// /// 测试:多线程并发获取单例服务,确保只创建一次 /// 演示容器的线程安全性 /// [TestMethod] public void Get_ConcurrentAccess_CreatesSingletonOnce() { // Arrange: 使用计数器追踪工厂方法调用次数 var factoryCallCount = 0; var createdInstances = new HashSet(); Global.RegisterSingleton(() => { Interlocked.Increment(ref factoryCallCount); Thread.Sleep(50); // 模拟复杂初始化 return new CacheService(); }); // Act: 多线程并发获取 var tasks = Enumerable.Range(0, 20).Select(i => Task.Run(() => { var service = Global.Get(); lock (createdInstances) { createdInstances.Add(service.GetHashCode()); } }) ).ToArray(); Task.WaitAll(tasks); // Assert: 工厂方法只被调用一次,所有线程获取的是同一个实例 Assert.AreEqual(1, factoryCallCount); Assert.AreEqual(1, createdInstances.Count); } /// /// 测试:多线程并发订阅和更新 /// [TestMethod] public void Subscribe_ConcurrentSubscribeAndUpdate_ThreadSafe() { // Arrange: 注册服务 Global.RegisterSingleton(new AppConfig { AppName = "App", Version = "1.0" }); var notificationCounts = new System.Collections.Concurrent.ConcurrentBag(); // Act: 多线程并发订阅 var subscribeTasks = Enumerable.Range(0, 10).Select(i => Task.Run(() => { var count = 0; Global.Subscribe(e => Interlocked.Increment(ref count)); notificationCounts.Add(count); }) ).ToArray(); Task.WaitAll(subscribeTasks); // Act: 触发更新 Global.Update(new AppConfig { AppName = "App", Version = "2.0" }); // 等待所有通知处理完成 Thread.Sleep(100); // Assert: 不应该抛出异常,所有订阅者都应该收到通知 Assert.AreEqual(10, notificationCounts.Count); } #endregion #region 实际应用场景测试 /// /// 场景测试:应用配置热更新 /// 演示实时配置更新的完整流程 /// [TestMethod] public void Scenario_LiveConfigurationUpdate() { // Arrange: 模拟配置变更历史 var configHistory = new List(); // 订阅配置变更 Global.Subscribe(e => { configHistory.Add($"v{e.OldValue?.Version} -> v{e.NewValue.Version}"); }); // Act: 注册初始配置 Global.RegisterSingleton(new AppConfig { AppName = "MyApp", Version = "1.0" }); // 模拟配置文件更新 Global.Update(new AppConfig { AppName = "MyApp", Version = "1.1" }); Global.Update(new AppConfig { AppName = "MyApp", Version = "1.2" }); Global.Update(new AppConfig { AppName = "MyApp", Version = "2.0" }); // Assert: 应该记录所有变更 Assert.AreEqual(3, configHistory.Count); Assert.AreEqual("v1.0 -> v1.1", configHistory[0]); Assert.AreEqual("v1.1 -> v1.2", configHistory[1]); Assert.AreEqual("v1.2 -> v2.0", configHistory[2]); // 当前配置应该是最新的 var currentConfig = Global.Get(); Assert.AreEqual("2.0", currentConfig.Version); } /// /// 场景测试:用户会话管理 /// 演示多组件监听用户会话变更 /// [TestMethod] public void Scenario_UserSessionManagement() { // Arrange: 模拟不同组件 var uiUpdates = new List(); var logEntries = new List(); // UI 组件订阅 Global.Subscribe(e => { uiUpdates.Add($"Display: {e.NewValue.Username}"); }); // 日志组件订阅 Global.Subscribe(e => { logEntries.Add($"Session changed at {e.ChangedAt:HH:mm:ss}"); }); // Act: 用户登录 Global.RegisterSingleton(new UserSession { Username = "user1", LoginTime = DateTime.Now }); // 用户切换 Thread.Sleep(10); Global.Update(new UserSession { Username = "user2", LoginTime = DateTime.Now }); // Assert: 各组件都应该收到通知 Assert.AreEqual(1, uiUpdates.Count); Assert.AreEqual("Display: user2", uiUpdates[0]); Assert.AreEqual(1, logEntries.Count); Assert.IsTrue(logEntries[0].StartsWith("Session changed at")); } #endregion #region 测试辅助类型 private class AppConfig { public string AppName { get; set; } = ""; public string Version { get; set; } = ""; } private class RequestContext { public string RequestId { get; set; } = Guid.NewGuid().ToString(); public DateTime Timestamp { get; set; } = DateTime.Now; } private interface ILogger { void Log(string message); } private class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine($"[LOG] {message}"); } } private class UserSession { public string Username { get; set; } = ""; public DateTime LoginTime { get; set; } } private class CacheService { private readonly Dictionary _cache = new(); } private class OperationContext { public Guid OperationId { get; } = Guid.NewGuid(); } #endregion }