mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-14 16:02: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.
|
||||
|
||||
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
|
||||
|
||||
`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 Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
|
||||
<None Include="..\.editorconfig" Link="Cli\.editorconfig" />
|
||||
<InternalsVisibleTo Include="$(AssemblyName).Tests" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -6,10 +6,6 @@
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\src\Spectre.Console\Cli\Internal\Constants.cs" Link="Imported\Constants.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Data\starwars.flf" />
|
||||
</ItemGroup>
|
||||
@ -24,7 +20,6 @@
|
||||
<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.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="Spectre.Verify.Extensions" Version="0.3.0" />
|
||||
<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