mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 08:52:50 +08:00
(#555) added TypeRegistrarBaseTests
as a very simple test harness to test implementations of ITypeRegistrar / ITypeResolver
This commit is contained in:
parent
2f6b4f53c4
commit
48df9cf68b
@ -74,6 +74,9 @@ return app.Run(args);
|
|||||||
|
|
||||||
`TypeRegistrar` is a custom class that must be created by the user. This [example using `Microsoft.Extensions.DependencyInjection` as the container](https://github.com/spectreconsole/spectre.console/tree/main/examples/Cli/Injection) provides an example `TypeRegistrar` and `TypeResolver` that can be added to your application with small adjustments for your DI container.
|
`TypeRegistrar` is a custom class that must be created by the user. This [example using `Microsoft.Extensions.DependencyInjection` as the container](https://github.com/spectreconsole/spectre.console/tree/main/examples/Cli/Injection) provides an example `TypeRegistrar` and `TypeResolver` that can be added to your application with small adjustments for your DI container.
|
||||||
|
|
||||||
|
Hint: If you do write your own implementation of `TypeRegistrar` and `TypeResolver` and you have some form of unit tests in place for your project,
|
||||||
|
there is a utility `TypeRegistrarBaseTests` available that can be used to ensure your implementations adhere to the required implementation. Simply call `TypeRegistrarBaseTests.RunAllTests()` and expect no `TypeRegistrarBaseTests.TestFailedException` to be thrown.
|
||||||
|
|
||||||
## Interception
|
## Interception
|
||||||
|
|
||||||
`CommandApp` also provides a `SetInterceptor` configuration. An interceptor is run before all commands are executed. This is typically used for configuring logging or other infrastructure concerns.
|
`CommandApp` also provides a `SetInterceptor` configuration. An interceptor is run before all commands are executed. This is typically used for configuring logging or other infrastructure concerns.
|
||||||
|
191
src/Spectre.Console.Testing/Cli/TypeRegistrarBaseTests.cs
Normal file
191
src/Spectre.Console.Testing/Cli/TypeRegistrarBaseTests.cs
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
using System;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Testing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is a utility class for implementors of
|
||||||
|
/// <see cref="ITypeRegistrar"/> and corresponding <see cref="ITypeResolver"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TypeRegistrarBaseTests
|
||||||
|
{
|
||||||
|
private readonly Func<ITypeRegistrar> _registrarFactory;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TypeRegistrarBaseTests"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="registrarFactory">The factory to create a new, clean <see cref="ITypeRegistrar"/> to be used for each test.</param>
|
||||||
|
public TypeRegistrarBaseTests(Func<ITypeRegistrar> registrarFactory)
|
||||||
|
{
|
||||||
|
_registrarFactory = registrarFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs all tests.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="TestFailedException">This exception is raised, if a test fails.</exception>
|
||||||
|
public void RunAllTests()
|
||||||
|
{
|
||||||
|
var testCases = new Action<ITypeRegistrar>[]
|
||||||
|
{
|
||||||
|
RegistrationsCanBeResolved,
|
||||||
|
InstanceRegistrationsCanBeResolved,
|
||||||
|
LazyRegistrationsCanBeResolved,
|
||||||
|
ResolvingNotRegisteredServiceReturnsNull,
|
||||||
|
ResolvingNullTypeReturnsNull,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var test in testCases)
|
||||||
|
{
|
||||||
|
test(_registrarFactory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ResolvingNullTypeReturnsNull(ITypeRegistrar registrar)
|
||||||
|
{
|
||||||
|
// Given no registration
|
||||||
|
var resolver = registrar.Build();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// When
|
||||||
|
var actual = resolver.Resolve(null);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if (actual != null)
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
$"Expected the resolver to resolve null, since null was requested as the service type. Actually resolved {actual.GetType().Name}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
$"Expected the resolver not to throw, but caught {ex.GetType().Name}.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ResolvingNotRegisteredServiceReturnsNull(ITypeRegistrar registrar)
|
||||||
|
{
|
||||||
|
// Given no registration
|
||||||
|
var resolver = registrar.Build();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// When
|
||||||
|
var actual = resolver.Resolve(typeof(IMockService));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if (actual != null)
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
$"Expected the resolver to resolve null, since no service was registered. Actually resolved {actual.GetType().Name}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
$"Expected the resolver not to throw, but caught {ex.GetType().Name}.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
registrar.Register(typeof(IMockService), typeof(MockService));
|
||||||
|
var resolver = registrar.Build();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var actual = resolver.Resolve(typeof(IMockService));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if (actual == null)
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
$"Expected the resolver to resolve an instance of {nameof(MockService)}. Actually resolved null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actual is not MockService)
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
$"Expected the resolver to resolve an instance of {nameof(MockService)}. Actually resolved {actual.GetType().Name}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InstanceRegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var instance = new MockService();
|
||||||
|
registrar.RegisterInstance(typeof(IMockService), instance);
|
||||||
|
var resolver = registrar.Build();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var actual = resolver.Resolve(typeof(IMockService));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if (!ReferenceEquals(actual, instance))
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
"Expected the resolver to resolve exactly the registered instance.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LazyRegistrationsCanBeResolved(ITypeRegistrar registrar)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var instance = new MockService();
|
||||||
|
var factoryCalled = false;
|
||||||
|
registrar.RegisterLazy(typeof(IMockService), () =>
|
||||||
|
{
|
||||||
|
factoryCalled = true;
|
||||||
|
return instance;
|
||||||
|
});
|
||||||
|
var resolver = registrar.Build();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var actual = resolver.Resolve(typeof(IMockService));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
if (!factoryCalled)
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
"Expected the factory to be called, to resolve the lazy registration.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReferenceEquals(actual, instance))
|
||||||
|
{
|
||||||
|
throw new TestFailedException(
|
||||||
|
"Expected the resolver to return exactly the result of the lazy-registered factory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// internal use only.
|
||||||
|
/// </summary>
|
||||||
|
private interface IMockService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockService : IMockService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exception, to be raised when a test fails.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TestFailedException : Exception
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="Exception" />
|
||||||
|
public TestFailedException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Exception" />
|
||||||
|
public TestFailedException(string message, Exception inner)
|
||||||
|
: base(message, inner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
<None Remove="Widgets\Figlet\Fonts\Standard.flf" />
|
<None Remove="Widgets\Figlet\Fonts\Standard.flf" />
|
||||||
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
|
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
|
||||||
<None Include="..\.editorconfig" Link="Cli\.editorconfig" />
|
<None Include="..\.editorconfig" Link="Cli\.editorconfig" />
|
||||||
|
<InternalsVisibleTo Include="$(AssemblyName).Tests" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -6,10 +6,6 @@
|
|||||||
<LangVersion>9.0</LangVersion>
|
<LangVersion>9.0</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="..\..\src\Spectre.Console\Cli\Internal\Constants.cs" Link="Imported\Constants.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Data\starwars.flf" />
|
<None Remove="Data\starwars.flf" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -24,7 +20,6 @@
|
|||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.1" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.1" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||||
<PackageReference Include="Nullable" Version="1.3.0" PrivateAssets="all" />
|
|
||||||
<PackageReference Include="Shouldly" Version="4.0.3" />
|
<PackageReference Include="Shouldly" Version="4.0.3" />
|
||||||
<PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" />
|
<PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" />
|
||||||
<PackageReference Include="Verify.Xunit" Version="9.0.0-beta.1" />
|
<PackageReference Include="Verify.Xunit" Version="9.0.0-beta.1" />
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
using Spectre.Console.Cli;
|
||||||
|
using Spectre.Console.Testing;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit.Cli
|
||||||
|
{
|
||||||
|
public sealed class DefaultTypeRegistrarTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Pass_Base_Registrar_Tests()
|
||||||
|
{
|
||||||
|
var harness = new TypeRegistrarBaseTests(() => new DefaultTypeRegistrar());
|
||||||
|
harness.RunAllTests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user