.net 如何为作为大型服务一部分的方法编写测试?

jyztefdp  于 2022-12-20  发布在  .NET
关注(0)|答案(2)|浏览(136)

我遇到了以下问题:我必须为一个方法编写一个测试,该方法是服务的一部分,但不使用所述服务的其他部分,除了两个方法(称为Remove,如下所示)。
方法我需要写一个测试来获取一个cookie名称。然后,基于这个cookie名称,它从字典中获取一个同意类别,说cookie属于。之后有一个IF语句,它使用HasCookieConsent方法,并确定是否应该删除cookie。如果是,它是由删除方法删除。

public void UpdateCookiesAccordingToConsent(string cookie)
{
    var cookiesConsentType = _httpOnlyCookies.FirstOrDefault(x => x.Key ==     
cookie).Value;

    if (!HasCookieConsent(cookiesConsentType) && _httpOnlyCookies.ContainsKey(cookie))
        {
            Remove(cookie);     
        }
}

Cookie类别取自字典:

private readonly Dictionary<string, CookiesConsentType> _httpOnlyCookies = new Dictionary<string, CookiesConsentType>()
{
    { CookieNames.VisitorCookieName,  CookiesConsentType.Statistic },
    { CookieNames.GoogleAnalyticsTrackingCookieName, CookiesConsentType.Statistic },
    { CookieNames.TaxonomyVisitorCookieName, CookiesConsentType.Statistic },
    { CookieNames.MarketoMunchkinTrackingCookieName, CookiesConsentType.Marketing },
};

删除方法:

public void Remove(string cookie)
{
    if (_httpContextAccessor.HttpContext == null)
    {
        return;
    }
    var options = new CookieOptions
    {
        HttpOnly = true,
            Secure = _httpContextAccessor.HttpContext.Request.IsHttps,
            Expires = DateTime.Now.AddDays(-1),
    };
    _httpContextAccessor.HttpContext.Response.Cookies.Append(cookie, string.Empty, options);
}

HasCookieConsent方法:

private bool HasCookieConsent(CookiesConsentType consentType)
{
try
{
    var hasConsentCookie = _httpContextAccessor?.HttpContext?.Request?.Cookies?.ContainsKey("CookieConsent") ?? false;
        if (!hasConsentCookie)
        {
            return false;
        }

    var cookie = _httpContextAccessor.HttpContext.Request.Cookies["CookieConsent"] ?? string.Empty;

        if (string.IsNullOrWhiteSpace(cookie))
        {
            return false;
        }

    var cookieConsent = JsonConvert.DeserializeObject<CookieConsent>(cookie) ?? new CookieConsent();
        return consentType switch
        {
            CookiesConsentType.Preferences => cookieConsent.Preferences,
            CookiesConsentType.Marketing => cookieConsent.Marketing,
            CookiesConsentType.Statistic => cookieConsent.Statistics,
            CookiesConsentType.Necessary => cookieConsent.Necessary,
            _ => false,
        };
    }
    catch (Exception ex)
    {
        _logger.LogError("Could not deserialize cookie: {Exception}", ex);
        return false;
    }
}

有什么窍门吗?我用的是xUnit。

r7knjye2

r7knjye21#

为了测试这个类,你需要创建一个HttpContext,它包含你想要的cookie请求,然后你需要一个IHttpContextAccessor返回HttpContext
看起来像是将IHttpContextAccessor注入到类中,如下所示:

public class YourClass
{
    private IHttpContextAccessor _httpContextAccessor;

    public YourClass(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    // all your other methods
}

在测试中,您将

  • 创建一个YourClass的示例并注入您创建的IHttpContextAccessor
  • 调用YourClass的方法
  • Assert响应cookie是您所期望的。

在这种情况下,您不需要创建任何模拟,可以使用现有的实现。
下面是一个测试的样子,它是模糊的,因为我没有尝试遵循你的代码做什么的细节,我不知道预期的结果应该是什么。

public void UpdateCookiesAccordingToConsent_does_whatever()
{
    // Create an HttpContext and add some request cookies to it
    var httpContext = new DefaultHttpContext();
    httpContext.Request.Cookies = new RequestCookieCollection(new Dictionary<string, string>()
    {
        { "cookieName", "cookieValue" },
        { "cookieName2", "cookieValue2" }
    });

    // Create an HttpContextAccessor that returns your HttpContext
    var contextAccessor = new HttpContextAccessor
    {
        HttpContext = httpContext
    };

    // Create an instance of your class. When it gets the current HttpContext
    // it will get the one you created.
    var testSubject = new YourClass(contextAccessor);

    // Call some method on your class.
    testSubject.UpdateCookiesAccordingToConsent("whatever");

    // Presumably executing that method has done something to the cookies.
    // Assert that the cookies in httpContext contain what you expect.
}

这里有另一种方法,这取决于你的编码风格。
如果其中一个方法中的逻辑变得非常复杂,或者您发现自己必须重用它,那么您可以将它移到一个单独的方法中,甚至是另一个类中的扩展。
下面是一个例子:

public static bool HasCookieConsent(this IRequestCookieCollection requestCookies)
{
    try
    {
        var hasConsentCookie = requestCookies?.ContainsKey("CookieConsent") ?? false;
        if (!hasConsentCookie)
        {
            return false;
        }

        var cookie = requestCookies["CookieConsent"] ?? string.Empty;

        if (string.IsNullOrWhiteSpace(cookie))
        {
            return false;
        }

        var cookieConsent = JsonConvert.DeserializeObject<CookieConsent>(cookie) ?? new CookieConsent();
        return consentType switch
        {
            CookiesConsentType.Preferences => cookieConsent.Preferences,
            CookiesConsentType.Marketing => cookieConsent.Marketing,
            CookiesConsentType.Statistic => cookieConsent.Statistics,
            CookiesConsentType.Necessary => cookieConsent.Necessary,
            _ => false,
        };
    }
    catch (Exception ex)
    {
        _logger.LogError("Could not deserialize cookie: {Exception}", ex);
        return false;
    }
}

这个方法被修改为不依赖于IHttpContextAccessorHttpContext,它不关心这些东西,它只关心cookie,所以这些cookie作为参数传递。
通过创建一个RequestCookiesCollection(如前一个示例),将其传递给该方法,并Assert该方法返回预期的true或false,可以很容易地测试该方法。
这是一个编码偏好。有些人喜欢有更多的私有方法,并通过公共方法来测试它们。在其他情况下,我们可能会将方法提取到它们自己的类中进行测试。在每种情况下,我都会选择让测试更容易编写的方法。我不是在推销其中的一个,只是提供选项。

sqserrrh

sqserrrh2#

试试这个。

[Test]
public void UpdateCookiesAccordingToConsent_CallsRemove_WhenHasCookieConsentIsFalseAndHttpOnlyCookiesContainsCookie()
{
    // Arrange
    var cookie = "testCookie";
    var cookiesConsentType = CookiesConsentType.Statistic;
    var httpOnlyCookies = new Dictionary<string, CookiesConsentType>()
    {
        { cookie, cookiesConsentType }
    };

    var service = new CookieService(httpOnlyCookies);
    service.HasCookieConsent(false);

    // Act
    service.UpdateCookiesAccordingToConsent(cookie);

    // Assert
    service.Remove(cookie);
}

相关问题