Reputation: 3555
I want to store a database connection string for my integration tests as a user secret. My project.json looks like this:
{
...
"dependencies": {
...
"Microsoft.Extensions.Configuration.UserSecrets": "1.1.0"
},
"tools": {
"Microsoft.Extensions.SecretManager.Tools": "1.1.0-preview4-final"
},
"userSecretsId": "dc5b4f9c-8b0e-4b99-9813-c86ce80c39e6"
}
I've added the following to the constructor of my test class:
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddUserSecrets();
However when I run the tests the following exception is thrown when it hits that line:
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'.
Have I missed something or is what I'm trying to do not supported?
Upvotes: 39
Views: 21855
Reputation: 28445
See instructions in https://patrickhuber.github.io/2017/07/26/avoid-secrets-in-dot-net-core-tests.html, in particular in InitialiseTest add
// 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()
However note that it will not allow to run tests on build server
Upvotes: 34
Reputation: 8156
My base test class initializes the ConfigurationBuilder, so knowing the assembly which has the userSecretsId is more tricky.
However we can determine all the assemblies invoked along the way, as follows
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
}
}
Advantage of this in my scenario is that if the developer assigns user secrets the tests will run locally correctly without any code change on their behalf.
Upvotes: 0
Reputation: 41
The other posts listed here have some great help for doing it all manually but VS2022 has automated it for you.
In Visual Studio 2022 just right click on the test project name and go to "Manage User Secrets".
If you have not installed Microsoft.Extensions.Configuration.UserSecrets it will prompt you that "...dependencies are needed. Would you like to add them..."
Click Yes and the secrets.json file will open up and you are off to the races.
(If you already have the UserSecrets installed it just opens up secrets.json.)
Here is a code snippet that worked great for me.
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." );
}
}
With a secrets.json of
{ "Cosmos": { "Uri": "Your Value Here", "Key": "Your Value Here" } }
Upvotes: 4
Reputation: 4813
You must specify the UserSecretsId in Startup
of your application.
[assembly: UserSecretsId("xxx")]
namespace myapp
{
public class Startup
{
...
Then you have to use the overload of .AddUserSecrets(Assembly assembly)
in your test project. Example:
.AddUserSecrets(typeof(Startup).GetTypeInfo().Assembly)
Source: https://stackoverflow.com/a/40775511/5270073
Upvotes: 8
Reputation: 2844
For settings you can use appsettings.json, not the project.json. It looks like this:
{
"userSecretsId": "dc5b4f9c-8b0e-4b99-9813-c86ce80c39e6"
}
Make sure to copy the file to output by changing the project.json:
"buildOptions": {
"copyToOutput": "appsettings.json"
}
Now you can retrieve the secret like this:
[Fact]
public MyTest()
{
var appSettings = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var secret = appSettings["userSecretsId"]
}
Upvotes: -5