如何为服务中的UpdateCookiesAccordingToConsent方法编写xUnit测试?
核心测试场景
要覆盖该方法的所有逻辑分支,需针对以下场景编写测试:
- 传入的Cookie名称不在
_httpOnlyCookies字典中,不执行移除操作 - 传入的Cookie名称在字典中,但用户已同意该类Cookie,不执行移除操作
- 传入的Cookie名称在字典中,且用户未同意该类Cookie,执行移除操作
依赖模拟准备
使用Moq框架模拟IHttpContextAccessor和ILogger<YourService>(替换YourService为实际服务类名),同时自定义MockResponseCookies类捕获Cookie移除操作的执行情况。
完整测试代码
using Xunit; using Moq; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; public class YourServiceTests { private readonly Mock<IHttpContextAccessor> _httpContextAccessorMock; private readonly Mock<ILogger<YourService>> _loggerMock; private readonly YourService _service; public YourServiceTests() { _httpContextAccessorMock = new Mock<IHttpContextAccessor>(); _loggerMock = new Mock<ILogger<YourService>>(); _service = new YourService(_httpContextAccessorMock.Object, _loggerMock.Object); } [Fact] public void UpdateCookiesAccordingToConsent_CookieNotInDictionary_DoesNotRemove() { // 准备:传入不存在的Cookie,设置任意同意状态 var nonExistentCookie = "UnknownCookie"; SetupHttpContextWithConsent(new CookieConsent { Statistics = false }); // 执行方法 _service.UpdateCookiesAccordingToConsent(nonExistentCookie); // 验证:未执行Cookie移除操作 var responseCookies = GetMockResponseCookies(); Assert.Empty(responseCookies.AppendedCookies); } [Fact] public void UpdateCookiesAccordingToConsent_CookieExistsWithConsent_DoesNotRemove() { // 准备:传入存在的统计类Cookie,设置用户已同意 var targetCookie = CookieNames.VisitorCookieName; SetupHttpContextWithConsent(new CookieConsent { Statistics = true }); // 执行方法 _service.UpdateCookiesAccordingToConsent(targetCookie); // 验证:未执行Cookie移除操作 var responseCookies = GetMockResponseCookies(); Assert.Empty(responseCookies.AppendedCookies); } [Fact] public void UpdateCookiesAccordingToConsent_CookieExistsWithoutConsent_RemovesCookie() { // 准备:传入存在的统计类Cookie,设置用户未同意 var targetCookie = CookieNames.VisitorCookieName; var consent = new CookieConsent { Statistics = false }; var responseCookies = new MockResponseCookies(); var httpContext = new DefaultHttpContext { Request = { Cookies = new RequestCookieCollection(new Dictionary<string, string> { { "CookieConsent", JsonConvert.SerializeObject(consent) } }) }, Response = { Cookies = responseCookies } }; _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContext); // 执行方法 _service.UpdateCookiesAccordingToConsent(targetCookie); // 验证:正确执行移除操作 Assert.Single(responseCookies.AppendedCookies); var appended = responseCookies.AppendedCookies.First(); Assert.Equal(targetCookie, appended.Key); Assert.Equal(string.Empty, appended.Value); Assert.True(appended.Options.HttpOnly); Assert.Equal(DateTime.Now.AddDays(-1).Date, appended.Options.Expires?.Date); } private void SetupHttpContextWithConsent(CookieConsent consent) { var consentJson = JsonConvert.SerializeObject(consent); var httpContext = new DefaultHttpContext { Request = { Cookies = new RequestCookieCollection(new Dictionary<string, string> { { "CookieConsent", consentJson } }) }, Response = { Cookies = new MockResponseCookies() } }; _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContext); } private MockResponseCookies GetMockResponseCookies() { return _httpContextAccessorMock.Object.HttpContext.Response.Cookies as MockResponseCookies; } } // 自定义类:捕获Response.Cookies的Append操作 public class MockResponseCookies : IResponseCookies { public List<(string Key, string Value, CookieOptions Options)> AppendedCookies { get; } = new(); public void Append(string key, string value) => AppendedCookies.Add((key, value, new CookieOptions())); public void Append(string key, string value, CookieOptions options) => AppendedCookies.Add((key, value, options)); public void Delete(string key) { } public void Delete(string key, CookieOptions options) { } } // 需与项目中实际类结构一致 public class CookieConsent { public bool Preferences { get; set; } public bool Marketing { get; set; } public bool Statistics { get; set; } public bool Necessary { get; set; } } public static class CookieNames { public const string VisitorCookieName = "VisitorCookie"; public const string GoogleAnalyticsTrackingCookieName = "GATrackingCookie"; public const string TaxonomyVisitorCookieName = "TaxonomyVisitorCookie"; public const string MarketoMunchkinTrackingCookieName = "MarketoMunchkinCookie"; } public enum CookiesConsentType { Preferences, Marketing, Statistic, Necessary }
关键说明
- 控制
HasCookieConsent返回值:通过在DefaultHttpContext的Request中设置CookieConsent的JSON值,间接控制私有方法HasCookieConsent的返回结果,无需反射调用私有方法。 - 验证移除操作:通过
MockResponseCookies记录Response.Cookies.Append的调用参数,验证Remove方法是否正确设置了过期Cookie。 - 分支覆盖:三个测试用例完整覆盖了方法的所有逻辑分支,确保方法行为符合预期。
内容的提问来源于stack exchange,提问作者cowefe
相关产品推荐
相关产品推荐

