.NET Core 2.x控制器Session测试问题及优化方案咨询
嘿,针对你遇到的这个.NET Core 2.x项目中Session存储Token且测试困难的问题,我给你几个实用的解决方案,帮你轻松搞定跨控制器存数据+易测试的需求:
方案1:用IMemoryCache替代Session(最易测试)
Session本质上也是基于缓存的,但直接用IMemoryCache更轻量,而且天生支持依赖注入,测试时Mock起来特别方便。
步骤:
- 注册服务:在
Startup.cs的ConfigureServices里添加内存缓存服务:
services.AddMemoryCache();
- 控制器注入使用:在Login控制器和其他需要访问Token的控制器中注入
IMemoryCache:
private readonly IMemoryCache _memoryCache; public LoginController(IMemoryCache memoryCache) { _memoryCache = memoryCache; }
- 存储Token:登录成功后直接存入缓存,还可以设置过期时间:
// 存储Token,2小时过期 _memoryCache.Set("UserToken", "your-actual-token", TimeSpan.FromHours(2));
- 跨控制器获取Token:在其他控制器中同样注入
IMemoryCache,然后取值:
if (_memoryCache.TryGetValue("UserToken", out string token)) { // 拿到Token做后续操作 }
- 测试时Mock:用Moq轻松模拟
IMemoryCache,不需要处理HttpContext:
var mockCache = new Mock<IMemoryCache>(); string testToken = "mock-test-token"; // 模拟TryGetValue的行为 mockCache.Setup(c => c.TryGetValue("UserToken", out It.Ref<string>.IsAny)) .Returns(true) .Callback((object key, out string value) => value = testToken); // 注入到控制器 var controller = new LoginController(mockCache.Object);
方案2:修复MockHttpSession的实现
如果你坚持要用Session,那问题出在你当前的MockHttpSession没有正确处理字符串的序列化/反序列化。你可以自己实现一个更完善的Mock Session:
自定义TestHttpSession:
public class TestHttpSession : ISession { private readonly Dictionary<string, byte[]> _storage = new Dictionary<string, byte[]>(); public string Id => "test-session-id"; public bool IsAvailable => true; public IEnumerable<string> Keys => _storage.Keys; public void Clear() => _storage.Clear(); public Task CommitAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; public Task LoadAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; public void Remove(string key) => _storage.Remove(key); public void Set(string key, byte[] value) => _storage[key] = value; public bool TryGetValue(string key, out byte[] value) => _storage.TryGetValue(key, out value); // 增加方便的字符串读写方法 public string GetString(string key) { if (TryGetValue(key, out var bytes)) { return Encoding.UTF8.GetString(bytes); } return null; } public void SetString(string key, string value) { Set(key, Encoding.UTF8.GetBytes(value)); } }
测试时使用:
var testSession = new TestHttpSession(); testSession.SetString("UserToken", "mock-token"); // 给控制器设置HttpContext var controller = new LoginController(); controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { Session = testSession } }; // 现在调用GetString就能拿到正确值了 var token = controller.HttpContext.Session.GetString("UserToken");
方案3:封装自定义会话服务(最推荐,符合DDD)
如果项目规模较大,建议封装一个抽象的会话服务,把存储逻辑和业务代码解耦,这样后续替换存储方式(比如从Session换成Redis)也不用改业务代码,测试也超简单。
步骤:
- 定义抽象接口:
public interface IUserSession { string GetUserToken(); void SetUserToken(string token); }
- 实现基于Session的版本:
public class SessionUserSession : IUserSession { private readonly IHttpContextAccessor _httpContextAccessor; public SessionUserSession(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public string GetUserToken() { return _httpContextAccessor.HttpContext?.Session.GetString("UserToken"); } public void SetUserToken(string token) { _httpContextAccessor.HttpContext?.Session.SetString("UserToken", token); } }
- 注册服务:在
Startup.cs中注册:
services.AddHttpContextAccessor(); services.AddScoped<IUserSession, SessionUserSession>();
- 控制器中使用:
private readonly IUserSession _userSession; public LoginController(IUserSession userSession) { _userSession = userSession; } // 存储Token _userSession.SetUserToken("your-token"); // 其他控制器获取Token var token = _userSession.GetUserToken();
- 测试时Mock:直接写一个测试用的实现,完全不用碰HttpContext:
public class MockUserSession : IUserSession { public string CurrentToken { get; set; } public string GetUserToken() => CurrentToken; public void SetUserToken(string token) => CurrentToken = token; } // 测试时使用 var mockSession = new MockUserSession { CurrentToken = "test-token" }; var controller = new LoginController(mockSession);
内容的提问来源于stack exchange,提问作者Enrico
相关产品推荐
相关产品推荐

