Skip to content
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
1 change: 1 addition & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ jobs:
dotnet pack ./Testing/NUnitUtility/Odin.Testing.NUnitUtility.csproj --configuration $CONFIGURATION --output $PACKAGE_DIR
dotnet pack ./Testing/XUnitUtility/Odin.Testing.XUnitUtility.csproj --configuration $CONFIGURATION --output $PACKAGE_DIR
dotnet pack ./Testing/XUnitV2Utility/Odin.Testing.XUnitV2Utility.csproj --configuration $CONFIGURATION --output $PACKAGE_DIR
dotnet pack ./Testing/Core/Odin.Testing.csproj --configuration $CONFIGURATION --output $PACKAGE_DIR
dotnet pack ./Testing/Fakes/Odin.Testing.Fakes.csproj --configuration $CONFIGURATION --output $PACKAGE_DIR
dotnet pack ./Templating/Razor.Abstractions/Odin.Templating.Razor.Abstractions.csproj --configuration $CONFIGURATION --output $PACKAGE_DIR
dotnet pack ./Templating/Razor/Odin.Templating.Razor.csproj --configuration $CONFIGURATION --output $PACKAGE_DIR
Expand Down
15 changes: 15 additions & 0 deletions Odin.sln
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{4F52FD
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Odin.Testing.XUnitV2Utility", "Testing\XUnitV2Utility\Odin.Testing.XUnitV2Utility.csproj", "{A1A296A9-4229-4603-9D7E-AC004A59C28C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Odin.Testing", "Testing\Core\Odin.Testing.csproj", "{CF698E79-D46D-4082-9CC2-EBE463877D50}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -687,6 +689,18 @@ Global
{A1A296A9-4229-4603-9D7E-AC004A59C28C}.Release|x64.Build.0 = Release|Any CPU
{A1A296A9-4229-4603-9D7E-AC004A59C28C}.Release|x86.ActiveCfg = Release|Any CPU
{A1A296A9-4229-4603-9D7E-AC004A59C28C}.Release|x86.Build.0 = Release|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Debug|x64.ActiveCfg = Debug|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Debug|x64.Build.0 = Debug|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Debug|x86.ActiveCfg = Debug|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Debug|x86.Build.0 = Debug|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Release|Any CPU.Build.0 = Release|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Release|x64.ActiveCfg = Release|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Release|x64.Build.0 = Release|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Release|x86.ActiveCfg = Release|Any CPU
{CF698E79-D46D-4082-9CC2-EBE463877D50}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -737,5 +751,6 @@ Global
{72478837-59C0-4E75-AEF8-8CD91D9C799E} = {3610DE71-0841-E98B-B5EB-0B69D343489C}
{06DE3EA8-C467-4BB0-8654-F3424709094A} = {3610DE71-0841-E98B-B5EB-0B69D343489C}
{A1A296A9-4229-4603-9D7E-AC004A59C28C} = {78DDA25A-7C38-13C4-DE39-740FEE45D7B1}
{CF698E79-D46D-4082-9CC2-EBE463877D50} = {78DDA25A-7C38-13C4-DE39-740FEE45D7B1}
EndGlobalSection
EndGlobal
Binary file modified Testing/.DS_Store
Binary file not shown.
14 changes: 14 additions & 0 deletions Testing/Core/IAssertionAdaptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Odin.Testing;

/// <summary>
/// Adapts framework-specific assertion APIs for shared testing utilities.
/// </summary>
public interface IAssertionAdaptor
{
/// <summary>
/// Asserts that a condition is true.
/// </summary>
/// <param name="condition">The condition that is expected to be true.</param>
/// <param name="message">The assertion failure message.</param>
void AssertTrue(bool condition, string message);
}
15 changes: 15 additions & 0 deletions Testing/Core/Odin.Testing.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageIcon>icon.png</PackageIcon>
<RootNamespace>Odin.Testing</RootNamespace>
<Description>Shared testing utility implementation.
</Description>
<WarningsAsErrors>1591;1573;</WarningsAsErrors> <!-- Not to be removed. Documentation is required. -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions"/>
</ItemGroup>
<ItemGroup>
<None Include="../../Assets/icon.png" Pack="true" PackagePath="" />
</ItemGroup>
</Project>
105 changes: 105 additions & 0 deletions Testing/Core/ServiceCollectionAssertions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Microsoft.Extensions.DependencyInjection;

namespace Odin.Testing;

/// <summary>
/// Methods for assertion of service registration in ServiceCollection.
/// </summary>
public static class ServiceCollectionAssertions
{
/// <summary>
/// Verifies service registration for a serviceType, lifetime and implementation type.
/// </summary>
/// <param name="services">The service collection to inspect.</param>
/// <param name="assertionAdaptor">The assertion adaptor to use for reporting failures.</param>
/// <param name="serviceType">The expected service type.</param>
/// <param name="specificLifetime">The expected service lifetime.</param>
/// <param name="implementationType">The expected implementation type.</param>
/// <param name="registrationCount">The expected number of matching registrations.</param>
public static void AssertServiceRegistration(ServiceCollection services, IAssertionAdaptor assertionAdaptor,
Type serviceType, ServiceLifetime specificLifetime, Type implementationType, int registrationCount = 1
)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(assertionAdaptor);
ArgumentNullException.ThrowIfNull(serviceType);
ArgumentNullException.ThrowIfNull(implementationType);

IReadOnlyList<ServiceDescriptor> found = services.Where(x =>
x.ServiceType == serviceType &&
x.ImplementationType == implementationType &&
x.Lifetime == specificLifetime).ToList();

assertionAdaptor.AssertTrue(found.Count == registrationCount,
$"Expected {registrationCount} registration(s) for {serviceType.Name} with lifetime {specificLifetime} " +
$"and implementation {implementationType.Name} but found {found.Count}. "
+ GetDescriptionOfAllServicesOfType(services, serviceType));
}

/// <summary>
/// Verifies service registration for a serviceType and lifetime.
/// </summary>
/// <param name="services">The service collection to inspect.</param>
/// <param name="assertionAdaptor">The assertion adaptor to use for reporting failures.</param>
/// <param name="serviceType">The expected service type.</param>
/// <param name="specificLifetime">The expected service lifetime.</param>
/// <param name="registrationCount">The expected number of matching registrations.</param>
public static void AssertServiceRegistration(ServiceCollection services, IAssertionAdaptor assertionAdaptor,
Type serviceType, ServiceLifetime specificLifetime, int registrationCount = 1
)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(assertionAdaptor);
ArgumentNullException.ThrowIfNull(serviceType);

IReadOnlyList<ServiceDescriptor> found = services.Where(x =>
x.ServiceType == serviceType &&
x.Lifetime == specificLifetime).ToList();

assertionAdaptor.AssertTrue(found.Count == registrationCount,
$"Expected {registrationCount} registration(s) for {serviceType.Name} with lifetime {specificLifetime} " +
$"but found {found.Count}. "
+ GetDescriptionOfAllServicesOfType(services, serviceType));
}

/// <summary>
/// Verifies service registration for a serviceType and implementation type with any lifetime.
/// </summary>
/// <param name="services">The service collection to inspect.</param>
/// <param name="assertionAdaptor">The assertion adaptor to use for reporting failures.</param>
/// <param name="serviceType">The expected service type.</param>
/// <param name="implementationType">The expected implementation type.</param>
/// <param name="registrationCount">The expected number of matching registrations.</param>
public static void AssertServiceRegistration(ServiceCollection services, IAssertionAdaptor assertionAdaptor,
Type serviceType, Type implementationType, int registrationCount = 1
)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(assertionAdaptor);
ArgumentNullException.ThrowIfNull(serviceType);
ArgumentNullException.ThrowIfNull(implementationType);

IReadOnlyList<ServiceDescriptor> found = services.Where(x =>
x.ServiceType == serviceType &&
x.ImplementationType == implementationType).ToList();

assertionAdaptor.AssertTrue(found.Count == registrationCount,
$"Expected {registrationCount} registration(s) for {serviceType.Name} with any lifetime " +
$"and implementation {implementationType.Name} but found {found.Count}. "
+ GetDescriptionOfAllServicesOfType(services, serviceType));
}

private static string GetDescriptionOfAllServicesOfType(ServiceCollection services, Type serviceType)
{
IReadOnlyList<ServiceDescriptor> allOfType = services
.Where(x => x.ServiceType == serviceType).ToList();

string serviceTypesFound = $"{(allOfType.Count == 0 ? "No other " : allOfType.Count)} registrations found for {serviceType.Name}";
if (allOfType.Count > 0)
{
serviceTypesFound += string.Join(Environment.NewLine + " - ", allOfType.Select(x => x.ToString()));
}

return serviceTypesFound;
}
}
15 changes: 15 additions & 0 deletions Testing/NUnitUtility/NUnitAssertionAdaptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using NUnit.Framework;

namespace Odin.Testing;

/// <summary>
/// Adapts NUnit assertions for shared Odin testing utilities.
/// </summary>
public sealed class NUnitAssertionAdaptor : IAssertionAdaptor
{
/// <inheritdoc />
public void AssertTrue(bool condition, string message)
{
Assert.That(condition, Is.True, message);
}
}
5 changes: 4 additions & 1 deletion Testing/NUnitUtility/Odin.Testing.NUnitUtility.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Odin.Testing.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="../../Assets/icon.png" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NUnit" />
</ItemGroup>
</Project>
</Project>
80 changes: 23 additions & 57 deletions Testing/NUnitUtility/ServiceCollectionAssertions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using NUnit.Framework;

// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection;

Expand All @@ -8,83 +6,51 @@ namespace Microsoft.Extensions.DependencyInjection;
/// </summary>
public static class ServiceCollectionAssertions
{
private static readonly Odin.Testing.IAssertionAdaptor AssertionAdaptor = new Odin.Testing.NUnitAssertionAdaptor();

/// <summary>
/// Verifies service registration for a serviceType, lifetime and implementation type.
/// </summary>
/// <param name="services"></param>
/// <param name="serviceType"></param>
/// <param name="implementationType"></param>
/// <param name="specificLifetime"></param>
/// <param name="registrationCount"></param>
/// <param name="services">The service collection to inspect.</param>
/// <param name="serviceType">The expected service type.</param>
/// <param name="implementationType">The expected implementation type.</param>
/// <param name="specificLifetime">The expected service lifetime.</param>
/// <param name="registrationCount">The expected number of matching registrations.</param>
public static void AssertServiceRegistration(this ServiceCollection services, Type serviceType,
ServiceLifetime specificLifetime, Type implementationType, int registrationCount = 1
)
{
IReadOnlyList<ServiceDescriptor> found = services.Where(x =>
x.ServiceType == serviceType &&
x.ImplementationType == implementationType &&
x.Lifetime == specificLifetime).ToList();

Assert.That(found.Count, Is.EqualTo(registrationCount),
$"Expected {registrationCount} registration(s) for {serviceType.Name} with lifetime {specificLifetime} " +
$"and implementation {implementationType.Name} but found {found.Count}. "
+ GetDescriptionOfAllServicesOfType(services, serviceType));

Odin.Testing.ServiceCollectionAssertions.AssertServiceRegistration(services, AssertionAdaptor,
serviceType, specificLifetime, implementationType, registrationCount);
}

/// <summary>
/// Verifies service registration for a serviceType and lifetime.
/// </summary>
/// <param name="services"></param>
/// <param name="serviceType"></param>
/// <param name="specificLifetime"></param>
/// <param name="registrationCount"></param>
/// <param name="services">The service collection to inspect.</param>
/// <param name="serviceType">The expected service type.</param>
/// <param name="specificLifetime">The expected service lifetime.</param>
/// <param name="registrationCount">The expected number of matching registrations.</param>
public static void AssertServiceRegistration(this ServiceCollection services, Type serviceType,
ServiceLifetime specificLifetime, int registrationCount = 1
)
{
IReadOnlyList<ServiceDescriptor> found = services.Where(x =>
x.ServiceType == serviceType &&
x.Lifetime == specificLifetime).ToList();

Assert.That(found.Count, Is.EqualTo(registrationCount),
$"Expected {registrationCount} registration(s) for {serviceType.Name} with lifetime {specificLifetime} " +
$"but found {found.Count}. "
+ GetDescriptionOfAllServicesOfType(services, serviceType));

Odin.Testing.ServiceCollectionAssertions.AssertServiceRegistration(services, AssertionAdaptor,
serviceType, specificLifetime, registrationCount);
}

/// <summary>
/// Verifies service registration for a serviceType and lifetime.
/// Verifies service registration for a serviceType and implementation type with any lifetime.
/// </summary>
/// <param name="services"></param>
/// <param name="serviceType"></param>
/// <param name="implementationType"></param>
/// <param name="registrationCount"></param>
/// <param name="services">The service collection to inspect.</param>
/// <param name="serviceType">The expected service type.</param>
/// <param name="implementationType">The expected implementation type.</param>
/// <param name="registrationCount">The expected number of matching registrations.</param>
public static void AssertServiceRegistration(this ServiceCollection services, Type serviceType,
Type implementationType, int registrationCount = 1
)
{
IReadOnlyList<ServiceDescriptor> found = services.Where(x =>
x.ServiceType == serviceType).ToList();

Assert.That(found.Count, Is.EqualTo(registrationCount),
$"Expected {registrationCount} registration(s) for {serviceType.Name} with any any lifetime " +
$"and implementation {implementationType.Name} but found {found.Count}. "
+ GetDescriptionOfAllServicesOfType(services, serviceType));
}

private static string GetDescriptionOfAllServicesOfType(ServiceCollection services, Type serviceType)
{
IReadOnlyList<ServiceDescriptor> allOfType = services
.Where(x => x.ServiceType == serviceType).ToList();

string serviceTypesFound = $"{(allOfType.Count == 0 ? "No other " : allOfType.Count)} registrations found for {serviceType.Name}";
if (allOfType.Count > 0)
{
serviceTypesFound += string.Join(Environment.NewLine + " - ", allOfType.Select(x => x.ToString()));
}
return serviceTypesFound;
Odin.Testing.ServiceCollectionAssertions.AssertServiceRegistration(services, AssertionAdaptor,
serviceType, implementationType, registrationCount);
}

}
}
3 changes: 3 additions & 0 deletions Testing/XUnitUtility/Odin.Testing.XUnitUtility.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions"/>
<PackageReference Include="xunit.v3.assert" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Odin.Testing.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="../../Assets/icon.png" Pack="true" PackagePath="" />
</ItemGroup>
Expand Down
Loading
Loading