Как выполнить модульное тестирование с помощью Moq хранилища ключей Azure

#c# #azure #unit-testing #moq #azure-keyvault

#c# #azure #модульное тестирование #moq #azure-keyvault

Вопрос:

Мне нужно смоделировать конечную точку хранилища ключей, чтобы узнать, вызываю ли я функцию для получения хранилища ключей один раз.

Я разрабатываю это с помощью C # и Moq (Framework) для проведения тестирования.

Интерфейс выглядит следующим образом:

 public interface IKeyVaultConnection
{
    string GetKeyVaultValue(string variableName);
}
 
  public class KeyVaultConnection
    {
        public KeyVaultClient keyVaultClient;
        private string endpointKeyVau<

        public KeyVaultConnection(string keyVaultAddress = "DefaultEndpoint")
        {
            AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
            keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
            endpointKeyVault = $"https://{ keyVaultAddress }.vault.azure.net";
        }

        private async Task<string> AsyncGetSecretValue(string keyName)
        {
            var secret = await keyVaultClient.GetSecretAsync($"{endpointKeyVault}/secrets/{ keyName }")
                    .ConfigureAwait(false);

            return secret.Value;
        }

        public string GetKeyVaultValue(string variableName)
        {
            Task<string> task = Task.Run(async () => await AsyncGetSecretValue(variableName));
            task.Wait();
            return task.Resu<
        }
}

 
  Mock<IKeyVaultConnection> mock = new Mock<IKeyVaultConnection>();
//----->>>>>>   Need to setup the endpoint
 mock.Setup(x => x.GetKeyVaultValue(It.IsAny<string>())).Returns(It.IsAny<string>());
 // mock.Verify(x => x.GetKeyVaultValue(It.IsAny<string>()), Times.Once());
 

Что мне нужно, так это подделать соединение с конечной точкой, чтобы получить, что я вызываю эту функцию один раз, и я получаю такие ошибки, как:

 "KeyVaultErrorException: Operation returned an invalid status code 'NotFound'"
 

за то, что не предоставил конечную точку.

Если я раскомментирую последнюю строку (mock.Verify(x => x.GetKeyVaultValue(It.IsAny<string>()), Times.Once());) , я получу это:

 "Expected invocation on the mock once, but was 0 times: 
 x => x.GetKeyVaultValue(It.IsAny<string>())"
 

Комментарии:

1. «некоторые ошибки» — вы можете быть более конкретными?

2. @stuartd отредактировано, но есть ошибки, например, вы не указали конечную точку и так далее…

3. Пожалуйста, поделитесь полным тестом, с sut также с вызовом… Что IKeyVaultWrapper такое? Кстати, вы не должны возвращать It.IsAny<string>() его null . Moq не знает, как вернуть какую-либо строку, что разумно, не так ли…

4. «Ожидаемый вызов в макете один раз, но был 0 раз» ну, в предоставленном вами коде вы его не вызываете, так что этого следовало ожидать?

5. И какой класс вы тестируете? Я думаю, проблема в том, что вы используете .Returns(It.IsAny<string>()) , что означает, что этот метод вернет null, тогда ваш класс sut на самом деле жалуется на это…

Ответ №1:

Если вы рассматриваете свой класс как не что иное, как скромную оболочку, от которой могут зависеть другие классы ISomethingThatReturnsValues , не зная об KeyVaultClient этом, тогда издеваться KeyVaultClient не нужно. Нам нужно только зайти так далеко. Нам не нужно модульное тестирование классов фреймворка, от которых мы зависим.

Другими словами, нужно ли нам проверять, что

 keyVaultClient.GetSecretAsync($"{endpointKeyVault}/secrets/{ keyName }")
 

…на самом деле вызывает конечную точку, чтобы получить секрет? Это так. Это то, что KeyVaultClient.GetSecretAsync(string) делает. Что мы можем сделать, если тест завершится неудачей? Мы не можем исправить этот класс. Аналогично, если бы мы следовали этому пути до его логического завершения, нам пришлось бы тестировать всевозможные вещи. Когда мы создаем List<string> и добавляем строку, она действительно добавляется? Мы не тестируем эти вещи, потому что они уже протестированы, поэтому разумно предположить, что они работают так, как ожидалось.

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

Часть удобства, связанная с отсутствием модульного тестирования класса, сводит его к минимуму, чтобы он действительно ничего не делал, кроме вызова внутреннего класса. В этом случае вы можете свернуть свой public private метод and в один public метод и, возможно, еще один, чтобы предоставить async опцию:

     public async Task<string> GetSecretValueAsync(string keyName)
    {
        return await keyVaultClient.GetSecretAsync($"{endpointKeyVault}/secrets/{ keyName }");
    }

    public string GetSecretValue(string keyName) => GetSecretValueAsync(keyName).Resu<
 

Теперь это более очевидно: этот класс на самом деле не делает ничего своего, что требует модульного тестирования. Его полезная цель — адаптироваться KeyVaultClient к вашему интерфейсу, IKeyVaultConnection чтобы другие классы не зависели напрямую KeyVaultClient .