如何在 dotnet core 测试项目中使用用户密钥。

32

我想将数据库连接字符串存储为用户机密,以供我的集成测试使用。我的project.json文件如下:

{
  ...

  "dependencies": {
    ...
    "Microsoft.Extensions.Configuration.UserSecrets": "1.1.0"        
  },

  "tools": {
    "Microsoft.Extensions.SecretManager.Tools": "1.1.0-preview4-final"
  },

  "userSecretsId": "dc5b4f9c-8b0e-4b99-9813-c86ce80c39e6"
}

我已经将以下内容添加到我的测试类构造函数中:
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddUserSecrets();

但是,当我运行测试时,当它遇到那行代码时,会抛出以下异常:

An exception of type 'System.InvalidOperationException' occurred in Microsoft.Extensions.Configuration.UserSecrets.dll but was not handled in user code

Additional information: Could not find 'UserSecretsIdAttribute' on assembly 'dotnet-test-nunit, Version=3.4.0.0, Culture=neutral, PublicKeyToken=null'.

我有遗漏什么吗,还是我试图做的不被支持?

5个回答

33

请查看https://patrickhuber.github.io/2017/07/26/avoid-secrets-in-dot-net-core-tests.html中的说明,特别是在InitialiseTest中添加。

// the type specified here is just so the secrets library can 
            // find the UserSecretId we added in the csproj file
            var builder = new ConfigurationBuilder()
                .AddUserSecrets<HttpClientTests>();

            Configuration = builder.Build()

请注意,它将不允许在构建服务器上运行测试。


1
请确保使用 Microsoft.Extensions.Configuration.UserSecrets nuget包,以便能够使用 AddUserSecrets 方法。 - Aage

8

您必须在应用程序的 Startup 中指定 UserSecretsId。

[assembly: UserSecretsId("xxx")]
namespace myapp
{
    public class Startup
    {
    ...

接下来您需要在测试项目中使用.AddUserSecrets(Assembly assembly)的重载。示例:

.AddUserSecrets(typeof(Startup).GetTypeInfo().Assembly)

来源:https://dev59.com/uZzha4cB1Zd3GeqPJ8P6#40775511

这篇文章介绍了如何在Java中使用SSL / TLS进行网络编程。 SSL / TLS是一种安全协议,可确保通过Internet传输的数据不会被窃取或篡改。 在Java中,可以使用javax.net.ssl包来实现SSL / TLS连接。


3

这里列出的其他帖子提供了一些手动操作的好帮助,但是VS2022已经为您自动化了。

在Visual Studio 2022中,只需右键单击测试项目名称,然后转到“管理用户机密”。

如果您尚未安装Microsoft.Extensions.Configuration.UserSecrets,则会提示您“...需要依赖项。您想添加它们吗...”

点击“是”,secrets.json文件将打开,然后您就可以开始了。

(如果您已经安装了UserSecrets,则只需打开secrets.json即可。)

这是一个对我非常有效的代码片段。

using FluentAssertions;
using Microsoft.Extensions.Configuration;
using Project.Cosmos;

namespace Project.Model.Tests;

public class CosmosDbRepository
    {
    private IConfiguration Configuration { get; }

    public CosmosDbRepository()
        {
        var builder = new ConfigurationBuilder()
            .AddUserSecrets< CosmosDbRepository >();

        Configuration = builder.Build();
        }

    [Fact]
    public async Task Should_connect_to_cosmos_db()
        {
        // Arrange
        var uri = Configuration[ "Cosmos:Uri" ];
        var key = Configuration[ "Cosmos:Key" ];
        var _repo = await CosmosRepository.CreateRepo( uri, key );
        // Act
        var account = await _repo.Client.ReadAccountAsync();
        // Assert
        account.Should().NotBeNull( "A valid connection should be able to query its account." );
        }
    }

使用以下内容的secrets.json

{ "Cosmos": { "Uri": "在此处输入您的值", "Key": "在此处输入您的值" } }


0
我的基础测试类初始化了ConfigurationBuilder,因此知道具有userSecretsId的程序集更加棘手。
但是我们可以按照以下方式确定沿途调用的所有程序集。
    var builder = new ConfigurationBuilder()
        // NOTE: Make the appsettings optional since we might just have a appsettings.TestConfig
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{environment}.json", optional: true)
        // NOTE: This brings in the test assembly's own settings as overrides for the base and environment values
        .AddJsonFile($"appsettings.TestConfig.json", optional: true)
        .AddEnvironmentVariables();
        
    var currentAssembly = Assembly.GetExecutingAssembly();
    var callerAssemblies = new StackTrace().GetFrames()
        .Select(x => x.GetMethod().ReflectedType.Assembly).Distinct()
        .Where(x => x.GetReferencedAssemblies().Any(y => y.FullName == currentAssembly.FullName));

    UserSecretsIdAttribute attribute = null;
    foreach (var assembly in callerAssemblies)
    {
        attribute = assembly.GetCustomAttribute<UserSecretsIdAttribute>();
        if (attribute != null)
        {
            break;
        }
    }

    if (attribute != null)
    {
        var userSecrets = attribute.UserSecretsId;

        // Wire up user secrets if we have them
        if (!string.IsNullOrEmpty(userSecrets))
        {
#if NETSTANDARD2_0
            builder.AddUserSecrets(userSecrets);
#else
            // NOTE: Your choice as to whether the secrets are optional or not
            builder.AddUserSecrets(userSecrets, true);
#endif
        }
    }

在我的情境下,这样做的好处是,如果开发者分配用户机密,测试将在本地正确运行,而不需要他们进行任何代码更改。

引发了 System.IO.NotFoundException 异常:'配置文件 'secrets.json' 未找到且不是可选的。物理路径为 'c:\project-path'。 - Felipe CS
要创建文件,您需要右键单击项目并选择管理用户机密,这将在正确的位置创建一个空文件。 - Paul Hatcher
更新了示例代码,使机密信息变为可选项,在 .NET 6.0 中有重大更改,请参见 https://github.com/dotnet/runtime/issues/61418。 - Paul Hatcher

-5

关于设置,您可以使用appsettings.json文件,而不是project.json文件。它看起来像这样:

{
    "userSecretsId": "dc5b4f9c-8b0e-4b99-9813-c86ce80c39e6"
}

请确保通过更改project.json将文件复制到输出目录:

"buildOptions": {
    "copyToOutput": "appsettings.json"
}

现在你可以像这样检索秘密:

[Fact]
public MyTest()
{
    var appSettings = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .Build();

    var secret = appSettings["userSecretsId"]
}

3
这似乎只是告诉我如何在ASP.NET项目中使用secrets,而这不是我正在做的。我已经有一个单独的Web项目,在其中成功地使用了secrets,我现在询问的是一个类库测试项目。 - Paul Hunt
抱歉,我忽略了那个问题,我会看看是否有解决方案。 - kloarubeek
这个应该可以解决问题。我认为您忘记了copyToOutput。 - kloarubeek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接