Использование HttpContext.GetTokenAsyn c в C# модульных тестах - PullRequest
3 голосов
/ 09 марта 2020

Я пытаюсь написать модульные тесты для моего класса контроллера, который извлекает токен с помощью следующей команды:

string token = await HttpContext.GetTokenAsync("access_token");

Поэтому я издевался над HttpContext с помощью следующего кода:

public static HttpContext MakeFakeContext()
{
    var serviceProvider = new Mock<IServiceProvider>();
    var authservice = new Mock<IAuthenticationService>();

    authservice.Setup(_ => _.GetTokenAsync(It.IsAny<HttpContext>(), It.IsAny<string>())).Returns(Task.FromResult("token"));
    serviceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authservice);

    return new DefaultHttpContext
    {
        RequestServices = serviceProvider.Object
    };
}

Я устанавливаю макет контекста с помощью:

var mockcontext = MakeFakeContext();

unitUnderTest.ControllerContext = new ControllerContext
{
    HttpContext = mockcontext
};

Теперь, когда я запускаю модульный тест, я получаю следующую ошибку:

System.NotSupportedException: Неподдерживаемое выражение: _ => _.GetTokenAsyn c (It.IsAny (), It.IsAny ()) Методы расширения (здесь: AuthenticationTokenExtensions.GetTokenAsyn c) нельзя использовать в выражениях настройки / проверки.

Во время моего исследования я наткнулся на решения, в которых можно смоделировать определенные c детали, которые находятся под капотом и не являются частью расширений. Вот некоторые из них: Moq IServiceProvider / IServiceScope , Как выполнить модульное тестирование HttpContext.SignInAsyn c ()? . Второй показывает похожую проблему, которая, кажется, работает после того, как я попробовал. Но по какой-то причине он не работает для метода GetTokenAsyn c.

Ребята, у вас есть какие-нибудь подсказки?

1 Ответ

2 голосов
/ 09 марта 2020

Вот исходный код для этого метода расширения:

public static Task<string> GetTokenAsync(this HttpContext context, string scheme, 
string tokenName) =>
    context.RequestServices.GetRequiredService<IAuthenticationService> 
    ().GetTokenAsync(context, scheme, tokenName);

, который в свою очередь вызывает этот метод расширения:

public static async Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string scheme, string tokenName)
{
    if (auth == null)
    {
        throw new ArgumentNullException(nameof(auth));
    }
    if (tokenName == null)
    {
        throw new ArgumentNullException(nameof(tokenName));
    }
    var result = await auth.AuthenticateAsync(context, scheme);
    return result?.Properties?.GetTokenValue(tokenName);
}

Конечный результат вызывает AuthenticateAsync, как видно из line var result = await auth.AuthenticateAsync(context, scheme);.

Поскольку вы не можете изменять методы расширения, возможно, вы можете написать свои собственные макеты?

Я не совсем уверен, что лучше всего делать макет над объектом, у которого есть методы расширения, поэтому, возможно, кто-то может расширить этот ответ.

Стоит отметить AuthenticateAsync не метод расширения, и вы можете найти код здесь .

Как упомянуто @Nkosi:

Mock IServiceProvider и IAuthenticationService.

ИМХО, видеть реальный код всегда полезно, так что вы можете определить и понять, что он делает под прикрытием, и полностью смоделировать все необходимые фрагменты, поэтому я оставлю выше вверх.

...