Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v4.1.0 #64

Merged
merged 3 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

Represents the **NuGet** versions.

## v4.1.0
- *Enhancement:* Removed the `FunctionsStartup` constraint for `TEntryPoint` to enable more generic usage.
- *Enhancement:* Enable `Microsoft.Azure.Functions.Worker.HttpTriggerAttribute` (new [_isolated_](https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide) function support), in addition to existing `Microsoft.Azure.WebJobs.HttpTriggerAttribute` (existing [_in-process_](https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library) function support), within `HttpTriggerTester`.
- *Enhancement:* Enable `Microsoft.Azure.Functions.Worker.ServiceBusTriggerAttribute` (new [_isolated_](https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide) function support), in addition to existing `Microsoft.Azure.WebJobs.ServiceBusTriggerAttribute` (existing [_in-process_](https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library) function support), within `ServiceBusTriggerTester`.
- Additionally, `CreateServiceBusMessageActions` is being renamed to `CreateWebJobsServiceBusMessageActions`; a new `CreateWorkerServiceBusMessageActions` has been introduced to support _isolated_ `Microsoft.Azure.Functions.Worker.ServiceBusTriggerAttribute` testing.
- *Enhancement*: Upgraded `NUnit` dependency to `4.0.1`; all unit tests now leverage the `NUnit` constraint model testing approach.
- **Note**: Also, as a result it is recommended prior to upgrading to `v4.1.0`, where using `NUnit`, that all existing unit tests are updated to use the new constraint model testing approach; see [migration guide](https://docs.nunit.org/articles/nunit/release-notes/Nunit4.0-MigrationGuide.html) for details.

## v4.0.1
- *Fixed:* The `FunctionTesterBase` was updated to correctly load the configuration in the order similar to that performed by the Azure Functions runtime fabric.
- *Fixed:* Removed all dependencies to `Newtonsoft.Json`; a developer will need to explicitly add this dependency and `IJsonSerializer` implementation where applicable.
Expand Down
2 changes: 1 addition & 1 deletion Common.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>4.0.1</Version>
<Version>4.1.0</Version>
<LangVersion>preview</LangVersion>
<Authors>Avanade</Authors>
<Company>Avanade</Company>
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

_UnitTestEx_ provides [.NET testing](https://docs.microsoft.com/en-us/dotnet/core/testing/) extensions to the most popular testing frameworks: [MSTest](https://github.com/Microsoft/testfx-docs), [NUnit](https://nunit.org/) and [Xunit](https://xunit.net/).

The scenarios that _UnitTestEx_ looks to address is the end-to-end unit-style testing of the following whereby the capabilities look to adhere to the AAA pattern of unit testing; Arrange, Act and Assert.
The scenarios that _UnitTestEx_ looks to address is the end-to-end unit-style testing of the following whereby the capabilities look to adhere to the _AAA_ pattern of unit testing; Arrange, Act and Assert.

- [API Controller](#API-Controller)
- [HTTP-triggered Azure Function](#HTTP-triggered-Azure-Function)
Expand All @@ -32,7 +32,7 @@ The included [change log](CHANGELOG.md) details all key changes per published ve

## API Controller

This leverages the [`WebApplicationFactory`](https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests) (WAF) as a means to host a test server in process to invoke APIs directly using HTTP requests. This has the benefit of validating the HTTP pipeline and all Dependency Injection (DI) configuration within. External system interactions can be mocked accordingly.
Leverages the [`WebApplicationFactory`](https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests) (WAF) as a means to host a test server in process to invoke APIs directly using HTTP requests. This has the benefit of validating the HTTP pipeline and all Dependency Injection (DI) configuration within. External system interactions can be mocked accordingly.

_UnitTestEx_ encapsulates the `WebApplicationFactory` providing a simple means to arrange the input, execute (act), and assert the response. The following is an [example](./tests/UnitTestEx.NUnit.Test/ProductControllerTest.cs).

Expand Down Expand Up @@ -62,13 +62,15 @@ test.ReplaceHttpClientFactory(mcf)
.Assert(new { id = "Abc", description = "A blue carrot" });
```

Both the [_Isolated worker model_](https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide) and [_In-process model_](https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library) are supported.

<br/>

## Service Bus-trigger Azure Function

As above, there is currently no easy means to integration (in-process) test Azure functions that rely on the [Azure Service Bus](https://azure.microsoft.com/en-us/services/service-bus/). _UnitTestEx_ looks to emulate by self-hosting the function, managing Dependency Injection (DI) configuration, and invocation of the specified method and verifies usage of the [`ServiceBusTriggerAttribute`](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus-trigger?tabs=csharp).

The following is an [example](./tests/UnitTestEx.NUnit.Test/ServiceBusFunctionTest.cs) of invoking the function method directly passing in a `ServiceBusReceivedMessage` created using `test.CreateServiceBusMessageFromValue` (this creates a message as if coming from Service Bus).
The following is an [example](./tests/UnitTestEx.NUnit.Test/ServiceBusFunctionTest.cs) of invoking the function method directly passing in a `ServiceBusReceivedMessage` created using `test.CreateServiceBusMessageFromValue` (this creates a message as if coming from Azure Service Bus).

``` csharp
using var test = FunctionTester.Create<Startup>();
Expand All @@ -78,6 +80,8 @@ test.ReplaceHttpClientFactory(mcf)
.AssertSuccess();
```

Both the [_Isolated worker model_](https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide) and [_In-process model_](https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library) are supported.

<br/>

## Generic Azure Function Type
Expand Down
3 changes: 1 addition & 2 deletions src/UnitTestEx.MSTest/FunctionTester.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/UnitTestEx

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using UnitTestEx.MSTest.Internal;
Expand All @@ -21,7 +20,7 @@ public static class FunctionTester
/// <param name="additionalConfiguration">Additional configuration values to add/override.</param>
/// <returns>The <see cref="ApiTester{TEntryPoint}"/>.</returns>
public static FunctionTester<TEntryPoint> Create<TEntryPoint>(bool? includeUnitTestConfiguration = null, bool? includeUserSecrets = null, IEnumerable<KeyValuePair<string, string?>>? additionalConfiguration = null)
where TEntryPoint : FunctionsStartup, new()
where TEntryPoint : class, new()
=> new(includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration);
}
}
3 changes: 1 addition & 2 deletions src/UnitTestEx.MSTest/Internal/FunctionTesterT.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/UnitTestEx

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using System.Collections.Generic;
using UnitTestEx.Functions;

Expand All @@ -14,5 +13,5 @@ namespace UnitTestEx.MSTest.Internal
/// <param name="includeUserSecrets">Indicates whether to include user secrets.</param>
/// <param name="additionalConfiguration">Additional configuration values to add/override.</param>
public class FunctionTester<TEntryPoint>(bool? includeUnitTestConfiguration, bool? includeUserSecrets, IEnumerable<KeyValuePair<string, string?>>? additionalConfiguration)
: FunctionTesterBase<TEntryPoint, FunctionTester<TEntryPoint>>(new MSTestImplementor(), includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration) where TEntryPoint : FunctionsStartup, new() { }
: FunctionTesterBase<TEntryPoint, FunctionTester<TEntryPoint>>(new MSTestImplementor(), includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration) where TEntryPoint : class, new() { }
}
2 changes: 1 addition & 1 deletion src/UnitTestEx.MSTest/UnitTestEx.MSTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MSTest.TestFramework" Version="3.0.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 2 additions & 3 deletions src/UnitTestEx.NUnit/FunctionTester.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/UnitTestEx

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using UnitTestEx.NUnit.Internal;
Expand All @@ -15,13 +14,13 @@ public static class FunctionTester
/// <summary>
/// Creates a new instance of the <see cref="ApiTester{TEntryPoint}"/> class.
/// </summary>
/// <typeparam name="TEntryPoint">The API startup <see cref="Type"/>.</typeparam>
/// <typeparam name="TEntryPoint">The Function startup <see cref="Type"/>.</typeparam>
/// <param name="includeUnitTestConfiguration">Indicates whether to include '<c>appsettings.unittest.json</c>' configuration file.</param>
/// <param name="includeUserSecrets">Indicates whether to include user secrets.</param>
/// <param name="additionalConfiguration">Additional configuration values to add/override.</param>
/// <returns>The <see cref="ApiTester{TEntryPoint}"/>.</returns>
public static FunctionTester<TEntryPoint> Create<TEntryPoint>(bool? includeUnitTestConfiguration = null, bool? includeUserSecrets = null, IEnumerable<KeyValuePair<string, string?>>? additionalConfiguration = null)
where TEntryPoint : FunctionsStartup, new()
where TEntryPoint : class, new()
=> new(includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration);
}
}
5 changes: 2 additions & 3 deletions src/UnitTestEx.NUnit/Internal/FunctionTesterT.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/UnitTestEx

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using System.Collections.Generic;
using UnitTestEx.Functions;

Expand All @@ -9,10 +8,10 @@ namespace UnitTestEx.NUnit.Internal
/// <summary>
/// Provides the <b>NUnit</b> <see cref="FunctionTesterBase{TEntryPoint, TSelf}"/> implementation.
/// </summary>
/// <typeparam name="TEntryPoint"></typeparam>
/// <typeparam name="TEntryPoint">The Function startup <see cref="System.Type"/>.</typeparam>
/// <param name="includeUnitTestConfiguration">Indicates whether to include '<c>appsettings.unittest.json</c>' configuration file.</param>
/// <param name="includeUserSecrets">Indicates whether to include user secrets.</param>
/// <param name="additionalConfiguration">Additional configuration values to add/override.</param>
public class FunctionTester<TEntryPoint>(bool? includeUnitTestConfiguration, bool? includeUserSecrets, IEnumerable<KeyValuePair<string, string?>>? additionalConfiguration)
: FunctionTesterBase<TEntryPoint, FunctionTester<TEntryPoint>>(new NUnitTestImplementor(), includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration) where TEntryPoint : FunctionsStartup, new() { }
: FunctionTesterBase<TEntryPoint, FunctionTester<TEntryPoint>>(new NUnitTestImplementor(), includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration) where TEntryPoint : class, new() { }
}
8 changes: 5 additions & 3 deletions src/UnitTestEx.NUnit/Internal/NUnitTestImplementor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ namespace UnitTestEx.NUnit.Internal
/// </summary>
public sealed class NUnitTestImplementor : Abstractions.TestFrameworkImplementor
{
private const string _notSpecifiedText = "Not specified.";

/// <summary>
/// Creates a <see cref="NUnitTestImplementor"/> .
/// </summary>
/// <returns>The <see cref="NUnitTestImplementor"/>.</returns>
public static NUnitTestImplementor Create() => new();

/// <inheritdoc/>
public override void AssertFail(string? message) => Assert.Fail(message);
public override void AssertFail(string? message) => Assert.Fail(message ?? _notSpecifiedText);

/// <inheritdoc/>
public override void AssertAreEqual<T>(T? expected, T? actual, string? message = null) where T : default => Assert.AreEqual(expected, actual, message, [expected, actual]);
public override void AssertAreEqual<T>(T? expected, T? actual, string? message = null) where T : default => Assert.That(actual, Is.EqualTo(expected), message);

/// <inheritdoc/>
public override void AssertInconclusive(string? message) => Assert.Inconclusive(message);
public override void AssertInconclusive(string? message) => Assert.Inconclusive(message ?? _notSpecifiedText);

/// <inheritdoc/>
public override void WriteLine(string? message) => TestContext.Out.WriteLine(message);
Expand Down
2 changes: 1 addition & 1 deletion src/UnitTestEx.NUnit/UnitTestEx.NUnit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit" Version="4.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 2 additions & 3 deletions src/UnitTestEx.Xunit/Internal/FunctionTesterT.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/UnitTestEx

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using System.Collections.Generic;
using UnitTestEx.Functions;
using Xunit.Abstractions;
Expand All @@ -10,11 +9,11 @@ namespace UnitTestEx.Xunit.Internal
/// <summary>
/// Provides the <b>MSTest</b> <see cref="FunctionTesterBase{TEntryPoint, TSelf}"/> implementation.
/// </summary>
/// <typeparam name="TEntryPoint"></typeparam>
/// <typeparam name="TEntryPoint">The Function startup <see cref="System.Type"/>.</typeparam>
/// <param name="output">The <see cref="ITestOutputHelper"/>.</param>
/// <param name="includeUnitTestConfiguration">Indicates whether to include '<c>appsettings.unittest.json</c>' configuration file.</param>
/// <param name="includeUserSecrets">Indicates whether to include user secrets.</param>
/// <param name="additionalConfiguration">Additional configuration values to add/override.</param>
public class FunctionTester<TEntryPoint>(ITestOutputHelper output, bool? includeUnitTestConfiguration, bool? includeUserSecrets, IEnumerable<KeyValuePair<string, string?>>? additionalConfiguration)
: FunctionTesterBase<TEntryPoint, FunctionTester<TEntryPoint>>(new XunitTestImplementor(output), includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration) where TEntryPoint : FunctionsStartup, new() { }
: FunctionTesterBase<TEntryPoint, FunctionTester<TEntryPoint>>(new XunitTestImplementor(output), includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration) where TEntryPoint : class, new() { }
}
7 changes: 3 additions & 4 deletions src/UnitTestEx.Xunit/UnitTestBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/UnitTestEx

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using UnitTestEx.Hosting;
Expand Down Expand Up @@ -35,15 +34,15 @@ public abstract class UnitTestBase(ITestOutputHelper output)
protected ApiTester<TEntryPoint> CreateApiTester<TEntryPoint>() where TEntryPoint : class => new(Output);

/// <summary>
/// Provides the <b>Xunit</b> API testing capability.
/// Provides the <b>Xunit</b> Functions testing capability.
/// </summary>
/// <typeparam name="TEntryPoint">The API startup <see cref="Type"/>.</typeparam>
/// <typeparam name="TEntryPoint">The Function startup <see cref="Type"/>.</typeparam>
/// <param name="includeUnitTestConfiguration">Indicates whether to include '<c>appsettings.unittest.json</c>' configuration file.</param>
/// <param name="includeUserSecrets">Indicates whether to include user secrets.</param>
/// <param name="additionalConfiguration">Additional configuration values to add/override.</param>
/// <returns>The <see cref="FunctionTester{TEntryPoint}"/>.</returns>
protected FunctionTester<TEntryPoint> CreateFunctionTester<TEntryPoint>(bool? includeUnitTestConfiguration = null, bool? includeUserSecrets = null, IEnumerable<KeyValuePair<string, string?>>? additionalConfiguration = null)
where TEntryPoint : FunctionsStartup, new()
where TEntryPoint : class, new()
=> new(Output, includeUnitTestConfiguration, includeUserSecrets, additionalConfiguration);

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/UnitTestEx.Xunit/UnitTestEx.Xunit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit" Version="2.6.5" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading