首页 > 解决方案 > 如何使用 Moq 对 Azure Key Vault 进行单元测试

问题描述

我需要模拟 Key Vault 的端点,以便知道我是否正在调用该函数来获取一次 Key Vault。

我正在使用 C# 和 Moq (Framework) 进行开发以进行测试。

界面如下:

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

        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.Result;
        }
}

 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>())"

标签: c#azureunit-testingmoqazure-keyvault

解决方案


如果您将您的类视为一个不起眼的包装器,以便其他类可以在ISomethingThatReturnsValues不知道的情况下依赖,KeyVaultClient那么模拟KeyVaultClient就没有必要了。我们只需要走这么远。我们不需要对我们依赖的框架类进行单元测试。

换句话说,我们是否需要验证

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

...实际上调用端点来获取秘密?确实如此。就是KeyVaultClient.GetSecretAsync(string)这样。如果测试失败,我们该怎么办?我们无法修复那个类。同样,如果我们沿着这条路得出其合乎逻辑的结论,我们将不得不测试各种东西。当我们创建一个List<string>并添加一个字符串时,它真的被添加了吗?我们不测试这些东西,因为它们已经过测试,因此可以合理地假设它们按预期工作。

一个很好的测试将是一个集成测试,它验证你的类是否符合它的预期。或者,如果另一个类依赖于此,您可以为该类编写一个集成测试,如果您无法从密钥库中检索任何内容,该测试将失败。但这不需要任何嘲笑。

对不对类进行单元测试感到满意的一部分是最小化它,以便它除了调用内部类之外什么都不做。在这种情况下,您可以将您的publicandprivate方法折叠成一种public方法,也许还有一种方法可以提供一个async选项:

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

    public string GetSecretValue(string keyName) => GetSecretValueAsync(keyName).Result;

现在更明显了:这个类本身并没有真正做任何需要单元测试的事情。它的有用目的是适应KeyVaultClient你的接口——IKeyVaultConnection这样其他类就不会直接依赖于KeyVaultClient.


推荐阅读