These steps are for getting .NET Core 2.1 MSTest tests to support building up the environment variables that an Azure Function uses both in a local environment and in Azure DevOps CI.
- Need to use live resources for integration testing because Event Hub doesn't have an emulator
- This means I have connection strings that must be treated like secrets (can't use
UseDevelopment=true
for everything)
- This means I have connection strings that must be treated like secrets (can't use
- Can't easily build up my own configuration using
ConfigurationBuilder
in the application because I'm using Azure Functions (and Webjobs)- Can't use something like this
- Need to be able to run locally and in Azure DevOps (VSTS)
// Azure Function
public static class Endpoint
{
private static string EhConnectionString { get; } =
Environment.GetEnvironmentVariable("EventHubConnectionString");
[FunctionName("Endpoint")]
public static async Task<IActionResult> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "post")]HttpRequest request, ILogger log)
{ /* do cool stuff */ }
}
// test fixture
[TestClass]
public class EndpointTests : FunctionTest
{
[TestInitialize]
public void Setup()
{
var settings = new ConfigurationBuilder()
.AddJsonFile("appsettings.test.json", false)
.Build();
foreach (var item in settings.AsEnumerable())
{
Environment.SetEnvironmentVariable(item.Key, item.Value);
}
}
// test methods
}
// local appsettings.test.json that is also gitignored
{
"EventHubConnectionString": "<ConnectionString>"
}
- Added Variable Group that included secrets
- Add
dotnet build
task to build definition (afterdotnet restore
task) with:- "Arguments" field:
--no-restore --output $(Build.BinariesDirectory)
- "Arguments" field:
- Add File Creator task to build definition (after
dotnet build
task) with:- "File path" field:
$(Build.BinariesDirectory)\appsettings.test.json
- "File content" field:
{ "EventHubConnectionString": "$(EventHubConnectionString)", }
- "File path" field:
- Add
dotnet test
task to run only integration test project within solution with:- "Arugments" field:
--no-restore --no-build --output $(Build.BinariesDirectory)
- "Arugments" field:
So final task list was:
dotnet restore
dotnet build
- File Creator
dotnet test
(integration test)
RUNSETTING ARGUMENTS DO NOT WORK AT ALL WITH DOTNET TEST (see open issue)
- Add
*.runsettings
file containing non-sensitive config to add to environment variables - Add test setup code that iterates through
TestContext#Properties
and sets environment variables
<!--mysettings.runsettings-->
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<TestRunParameters>
<Parameter name="AzureWebJobsStorage" value="UseDevelopmentStorage=true" />
<Parameter name="MyNonSensitiveEnvVarSetting" value="foobar" />
</TestRunParameters>
</RunSettings>
// test fixture
public TestContext TestContext { get; set; }
[TestInitialize]
public void Setup()
{
foreach (var item in TestContext.Properties)
{
Environment.SetEnvironmentVariable(item.Key.Replace(EnvVarPrefix, string.Empty), item.Value.ToString());
}
}
I know this is not a forum, but anyway I would like to mention my findings about this strategy, focusing on the DevOps part.
As far as I could see, the
--no-restore --no-build
parameters are not needed.The
--output
parameter is only there to make sure all steps work in the same directory. For some reason that I could not resolve, in my solution some project dependencies are not found when I use the explicit output directory. Therefore my solution was to identify the default working directory of my test step and to create the appsettings file right there. Not beautiful, but working.