JoachimDalen.AzureFunctions.TestUtils
0.0.1-preview
See the version list below for details.
dotnet add package JoachimDalen.AzureFunctions.TestUtils --version 0.0.1-preview
NuGet\Install-Package JoachimDalen.AzureFunctions.TestUtils -Version 0.0.1-preview
<PackageReference Include="JoachimDalen.AzureFunctions.TestUtils" Version="0.0.1-preview" />
paket add JoachimDalen.AzureFunctions.TestUtils --version 0.0.1-preview
#r "nuget: JoachimDalen.AzureFunctions.TestUtils, 0.0.1-preview"
// Install JoachimDalen.AzureFunctions.TestUtils as a Cake Addin #addin nuget:?package=JoachimDalen.AzureFunctions.TestUtils&version=0.0.1-preview&prerelease // Install JoachimDalen.AzureFunctions.TestUtils as a Cake Tool #tool nuget:?package=JoachimDalen.AzureFunctions.TestUtils&version=0.0.1-preview&prerelease
Azure Functions Test Utils
MSTest wrapper for Azure Functions Core Tools for easy bootstrapping of integration tests.
Currently under development to make things work together. When all the intial functionality is in place, a refactor will be done to clean some things up.
Getting started
Add the following base class BaseFunctionTest to your test project, this allows us to hook into MSTest. All your test needs to inherit from this class.
In the AssemblyInit
method, set your function app output path, e.g src/SampleFunctions/bin/Debug/netcoreapp3.1
.
On startup, paths for FuncHostPath
(azure-functions-core-tools) and DotNetPath
will try to be resolved from the system if they are left unconfigured.
Planned support
- Running function host (In progress)
- Running Azurite for storage account emulation (In progress)
Future:
- Running CosmosDB emulator
- Support for running side services as Docker containers
Example
using System.Net;
using System.Threading.Tasks;
using AzureFunctions.TestUtils.Attributes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SampleFunctions.Functions;
namespace SampleFunctions.IntegrationTests
{
[TestClass]
public class GetHelloTests : BaseFunctionTest
{
[TestMethod]
/**
Starts a singe function instead of all. Omit this attribute to run all fuctions in the app.
**/
[StartFunctions(nameof(GetHello))]
public async Task GetHello_NoQueryParams_ReturnsBadRequest()
{
var response = await Fixture.Client.GetAsync("/api/hello");
Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
}
[TestMethod]
[StartFunctions(nameof(GetHello))]
public async Task GetHello_QueryParams_ReturnsOk()
{
var response = await Fixture.Client.GetAsync("/api/hello?name=James");
Assert.IsTrue(response.IsSuccessStatusCode);
var responseText = await response.Content.ReadAsStringAsync();
Assert.AreEqual("Hello, James", responseText);
}
}
}
Usage
Starting functions
The StartFunctions
attribute allows you to select functions to run for the test.
Run a single function
[TestMethod]
[StartFunctions(nameof(GetOrder))]
public async Task GerOrder_Returns_Ok()
{
var response = await Fixture.Client.GetAsync("/api/order/2556");
Assert.IsTrue(response.IsSuccessStatusCode);
}
Run multiple functions
[TestMethod]
[StartFunctions(nameof(GetOrders), nameof(GetOrderItems))]
public async Task GetOrderItems_Returns_Ok()
{
var response = await Fixture.Client.GetAsync("/api/orders");
var responseText = await response.Content.ReadAsStringAsync();
dynamic reponseObj = JsonConvert.DeserializeObject(responseText);
var orderId = responseObj[0].orderId;
var itemsResponse = await Fixture.Client.GetAsync($"/api/order/{orderId}/items");
Assert.IsTrue(response.itemsResponse);
}
Authorization
By default function authorization is not enabled when running locally. This can be enabled by adding the attribute UseFunctionAuth
. Function and host keys can be pre generated using the attributes UseFunctionKey
.
[TestMethod]
[UseFunctionAuth]
[StartFunctions(nameof(GetHello))]
[UseFunctionKey(FunctionAuthLevel.Function, "main", nameof(GetHello), "helloValue")]
public async Task GetHello_Key_ReturnsOk()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Get, "/api/hello?name=James");
httpRequest.Headers.Add(AzureFunctionConstants.FunctionKeyHeaderName, "helloValue");
var response = await Fixture.Client.SendAsync(httpRequest);
Assert.IsTrue(response.IsSuccessStatusCode);
}
Scoped function key
[UseFunctionKey(FunctionAuthLevel.Function, "main", nameof(GetHello), "value1")]
Global function key
[UseFunctionKey(FunctionAuthLevel.Function, "global-key", "value1")]
Master key
[UseFunctionKey(FunctionAuthLevel.Admin, "ignored-value", "value1")]
System key
[UseFunctionKey(FunctionAuthLevel.System, "system-key", "value1")]
Storage
When running Azurite, you can also initialize the emulator with Blob Containers, Queues and Tables.
[TestMethod]
[StartFunctions(nameof(CreateOrder), nameof(SendOrderConfirmation))]
[UseQueues("orders")]
[UseBlobContainers("order-confirmations")]
public async Task CreateOrder_Sends_Confirmation()
{
var response = await Fixture.Client.PostAsync("/api/orders", new StringContent(""));
await Task.Delay(5000); // Wait for message to be sent on queue
Assert.IsTrue(response.IsSuccessStatusCode);
Assert.That.BlobExists("UseDevelopmentStorage=true", "order-confirmations", "order-1.json");
}
Custom Asserts
Area | Assert | Description |
---|---|---|
Blob | BlobExists | Assert that a given blob item exists in the container |
Blob | ContainerExists | Assert that a blob container exists |
Queue | QueueIsEmpty | Assert that the given queue does not contain any messages |
Queue | QueueHasMessageCount | Assert that the given queue contains the given amount of messages |
Queue | QueueHasMessage | Assert that the queue contains at least one message matching the expression |
Settings
Setting | Environment | Description |
---|---|---|
DotNetPath |
AFTU_DOT_NET_PATH |
Path to dotnet executable |
FuncHostPath |
AFTU_FUNC_HOST_PATH |
Path to func.dll |
FuncAppPath |
AFTU_FUNC_APP_PATH |
Directory path to function app to run |
AzuritePath |
AFTU_AZURITE_PATH |
Path to Azurite executable |
FuncHostPort |
AFTU_FUNC_HOST_PORT |
Port to run function app on |
DataDirectory |
AFTU_DATA_DIRECTORY |
Directory to store Azurite data in. Defaults to TestResults directory for the given test |
UseAzuriteStorage |
AFTU_USE_AZURITE_STORAGE |
Use azurite storage emulator. This should be true if using any storage related attributes |
RunAzurite |
AFTU_RUN_AZURITE |
Start Azurite alongside function app host |
PersistAzureContainers |
AFTU_PERSIST_AZURE_CONTAINERS |
Decides if containers created by the function app should be persisted after test run. |
ClearStorageAfterRun |
AFTU_CLEAR_STORAGE_AFTER_RUN |
Clears Azurite after each test run and provides a clean container for new runs |
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.1 is compatible. |
-
.NETCoreApp 3.1
- Azure.Data.Tables (>= 12.1.0)
- Azure.Storage.Blobs (>= 12.9.1)
- Azure.Storage.Queues (>= 12.7.0)
- Microsoft.NET.Test.Sdk (>= 16.7.1)
- MSTest.TestFramework (>= 2.1.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.1.0-preview | 244 | 10/9/2021 |
0.0.1-preview | 157 | 10/2/2021 |