被測試程式碼
public static VMERPUser Me {
get {
var identity = HttpContext.Current.User.Identity as FormsIdentity;
if (identity != null) {
var ticket = identity.Ticket;
return JsonConvert.DeserializeObject(ticket.UserData);
}
return null;
}
}
Moq 完整測試程式碼
[TestMethod]
public void Me_ReturnsUser_WhenValidTicket() {
// 1. 準備假資料
var userData = JsonConvert.SerializeObject(new VMERPUser { Id = 1, Name = "Test" });
var ticket = new FormsAuthenticationTicket(1, "user", DateTime.Now, DateTime.Now.AddHours(1), false, userData);
var identity = new FormsIdentity(ticket);
var principal = new GenericPrincipal(identity, new string[0]);
// 2. Mock HttpContext
var mockContext = new Mock();
mockContext.Setup(c => c.User).Returns(principal);
mockContext.Setup(c => c.Request).Returns(new Mock().Object);
mockContext.Setup(c => c.Response).Returns(new Mock().Object);
// 3. 設定 Current (靜態屬性需用 HttpContext.Current = ...)
HttpContext.Current = new HttpContext(
new SimpleWorkerRequest("", "", new StringWriter()),
mockContext.Object
);
// 或使用 MvcContrib.TestHelper 的 TestControllerBuilder
// 4. 驗證
var result = YourClass.Me;
Assert.IsNotNull(result);
Assert.AreEqual(1, result.Id);
Assert.AreEqual("Test", result.Name);
}
關鍵點解析
| 步驟 | 說明 |
| FormsAuthenticationTicket | UserData 欄位存放序列化後的使用者物件 |
| FormsIdentity | 包裝 Ticket,實作 IIdentity |
| GenericPrincipal | 包裝 Identity + 角色陣列 |
| HttpContext.Current | 靜態屬性,測試前需手動注入 Mock 物件 |
現代替代方案 (ASP.NET Core)
// Controller 內直接注入 IHttpContextAccessor
public class MyController : Controller {
private readonly IHttpContextAccessor _http;
public MyController(IHttpContextAccessor http) => _http = http;
public VMERPUser Me =>
_http.HttpContext?.User?.Identity is FormsIdentity id
? JsonConvert.DeserializeObject(((FormsIdentity)id).Ticket.UserData)
: null;
}
// 測試時直接 Mock IHttpContextAccessor
var mockAccessor = new Mock();
mockAccessor.Setup(a => a.HttpContext.User).Returns(principal);
常見坑
- ❌ 忘記
HttpContext.Current = ... → NullReferenceException
- ❌
SimpleWorkerRequest 建構子參數錯誤 → 導致 Context 初始化失敗
- ✅ 使用
MvcContrib.TestHelper 或 AspNetCore.TestHost 簡化設定