-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* テストしやすいようにConsoleAppContextの生成処理を切り出し * TestLoggerManagerをTestBaseから取得できるように修正 * 起動パラメーターの情報がログに残ることを確認するテストを追加 * ConsoleAppContextFactoryを追加 * ホストの開始および終了に関するログ出力のテストコードを追加 * テストフレームワークの名称変更に対応
- Loading branch information
1 parent
f59b0e2
commit 3d2d149
Showing
8 changed files
with
440 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
+205 Bytes
(100%)
samples/ConsoleAppWithDI/solution/readme-images/load-projects-to-solution.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions
82
samples/ConsoleAppWithDI/solution/src/Maris.ConsoleApp.Hosting/ConsoleAppContextFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
using CommandLine; | ||
using Maris.ConsoleApp.Core; | ||
using Maris.ConsoleApp.Hosting.Resources; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Maris.ConsoleApp.Hosting; | ||
|
||
/// <summary> | ||
/// <see cref="ConsoleAppContext"/> のインスタンスを生成するためのファクトリクラスです。 | ||
/// </summary> | ||
internal class ConsoleAppContextFactory | ||
{ | ||
private readonly IApplicationProcess appProcess; | ||
private readonly ConsoleAppSettings settings; | ||
private readonly ILogger<ConsoleAppContextFactory> logger; | ||
|
||
/// <summary> | ||
/// <see cref="ConsoleAppContextFactory"/> クラスの新しいインスタンスを初期化します。 | ||
/// </summary> | ||
/// <param name="appProcess">アプリケーションのプロセスを表すインターフェース。</param> | ||
/// <param name="settings">コンソールアプリケーションの設定。</param> | ||
/// <param name="logger">ロガー。</param> | ||
/// <exception cref="ArgumentNullException"> | ||
/// <list type="bullet"> | ||
/// <item><paramref name="appProcess"/> が <see langword="null"/> です。</item> | ||
/// <item><paramref name="settings"/> が <see langword="null"/> です。</item> | ||
/// <item><paramref name="logger"/> が <see langword="null"/> です。</item> | ||
/// </list> | ||
/// </exception> | ||
public ConsoleAppContextFactory(IApplicationProcess appProcess, ConsoleAppSettings settings, ILogger<ConsoleAppContextFactory> logger) | ||
{ | ||
this.appProcess = appProcess ?? throw new ArgumentNullException(nameof(appProcess)); | ||
this.settings = settings ?? throw new ArgumentNullException(nameof(settings)); | ||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
} | ||
|
||
/// <summary> | ||
/// コンソールアプリケーションの実行コンテキストを生成します。 | ||
/// 単体テスト用に公開しています。 | ||
/// </summary> | ||
/// <param name="args">コンソールアプリケーションの起動引数。</param> | ||
/// <param name="commandParametersOption"> | ||
/// コマンドの名前とコマンドの型を管理するコレクションのオプション設定を実行します。 | ||
/// 既定値は <see langword="null"/> です。 | ||
/// </param> | ||
/// <returns>生成した <see cref="CreateConsoleAppContext"/> 。</returns> | ||
/// <exception cref="InvalidOperationException"> | ||
/// <list type="bullet"> | ||
/// <item>コマンドパラメーターの型が見つかりません。</item> | ||
/// </list> | ||
/// </exception> | ||
internal ConsoleAppContext CreateConsoleAppContext( | ||
IEnumerable<string> args, | ||
Action<CommandParameterTypeCollection>? commandParametersOption) | ||
{ | ||
this.logger.LogInformation(Messages.ParseParameter.Embed(string.Join(' ', args))); | ||
var commandParameterTypes = new CommandParameterTypeCollection(); | ||
if (commandParametersOption is null) | ||
{ | ||
commandParameterTypes.InitializeFromAllAssemblies(); | ||
} | ||
else | ||
{ | ||
commandParametersOption(commandParameterTypes); | ||
} | ||
|
||
if (!commandParameterTypes.Any()) | ||
{ | ||
var assemblies = string.Join(',', commandParameterTypes.LoadedAssemblies.Select(asm => asm.GetName().Name)); | ||
throw new InvalidOperationException( | ||
Messages.CommandParameterIsNotExists.Embed(typeof(CommandAttribute), assemblies)); | ||
} | ||
|
||
var param = Parser.Default.ParseArguments(args, commandParameterTypes.ToArray()); | ||
if (param is null || param.Tag == ParserResultType.NotParsed) | ||
{ | ||
this.appProcess.Exit(this.settings.DefaultValidationErrorExitCode); | ||
} | ||
|
||
return new ConsoleAppContext(param.Value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
...pWithDI/solution/tests/Maris.ConsoleApp.UnitTests/Hosting/ConsoleAppContextFactoryTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Reflection; | ||
using CommandLine; | ||
using Maris.ConsoleApp.Core; | ||
using Maris.ConsoleApp.Hosting; | ||
using Microsoft.Extensions.Logging; | ||
using Xunit.Abstractions; | ||
|
||
namespace Maris.ConsoleApp.UnitTests.Hosting; | ||
|
||
public class ConsoleAppContextFactoryTest(ITestOutputHelper testOutputHelper) : TestBase(testOutputHelper) | ||
{ | ||
[Fact] | ||
public void Constructor_IApplicationProcessがnull_ArgumentNullExceptionが発生する() | ||
{ | ||
// Arrange | ||
IApplicationProcess? appProcess = null; | ||
var settings = new ConsoleAppSettings(); | ||
var logger = this.CreateTestLogger<ConsoleAppContextFactory>(); | ||
|
||
// Act | ||
var action = () => new ConsoleAppContextFactory(appProcess!, settings, logger); | ||
|
||
// Assert | ||
Assert.Throws<ArgumentNullException>("appProcess", action); | ||
} | ||
|
||
[Fact] | ||
public void Constructor_ConsoleAppSettingsがnull_ArgumentNullExceptionが発生する() | ||
{ | ||
// Arrange | ||
var appProcess = new TestApplicationProcess(); | ||
ConsoleAppSettings? settings = null; | ||
var logger = this.CreateTestLogger<ConsoleAppContextFactory>(); | ||
|
||
// Act | ||
var action = () => new ConsoleAppContextFactory(appProcess, settings!, logger); | ||
|
||
// Assert | ||
Assert.Throws<ArgumentNullException>("settings", action); | ||
} | ||
|
||
[Fact] | ||
public void Constructor_ILoggerがnull_ArgumentNullExceptionが発生する() | ||
{ | ||
// Arrange | ||
var appProcess = new TestApplicationProcess(); | ||
var settings = new ConsoleAppSettings(); | ||
ILogger<ConsoleAppContextFactory>? logger = null; | ||
|
||
// Act | ||
var action = () => new ConsoleAppContextFactory(appProcess, settings, logger!); | ||
|
||
// Assert | ||
Assert.Throws<ArgumentNullException>("logger", action); | ||
} | ||
|
||
[Fact] | ||
public void CreateConsoleAppContext_起動パラメーターが情報レベルでログに出力される() | ||
{ | ||
// Arrange | ||
var args = new string[] { "console-app-context-factory-test", "--category-id", "123" }; | ||
var types = new Type[] { typeof(TestParameter) }; | ||
var assembly = new TestAssembly(types); | ||
Action<CommandParameterTypeCollection>? commandParametersOption = collection => collection.AddCommandParameterTypeFrom(assembly); | ||
var appProcess = new TestApplicationProcess(); | ||
var settings = new ConsoleAppSettings(); | ||
var logger = this.CreateTestLogger<ConsoleAppContextFactory>(); | ||
var factory = new ConsoleAppContextFactory(appProcess, settings, logger); | ||
|
||
// Act | ||
_ = factory.CreateConsoleAppContext(args, commandParametersOption); | ||
|
||
// Assert | ||
Assert.Equal(1, this.LogCollector.Count); | ||
var record = this.LogCollector.LatestRecord; | ||
Assert.Equal(LogLevel.Information, record.Level); | ||
Assert.Equal(default, record.Id); | ||
Assert.Equal("起動パラメーター:console-app-context-factory-test --category-id 123 のパースを行います。", record.Message); | ||
} | ||
|
||
[Fact] | ||
public void CreateConsoleAppContext_正常系_起動パラメーターの情報が含まれたコンテキストを生成できる() | ||
{ | ||
// Arrange | ||
var args = new string[] { "console-app-context-factory-test", "--category-id", "123" }; | ||
var types = new Type[] { typeof(TestParameter) }; | ||
var assembly = new TestAssembly(types); | ||
Action<CommandParameterTypeCollection>? commandParametersOption = collection => collection.AddCommandParameterTypeFrom(assembly); | ||
var appProcess = new TestApplicationProcess(); | ||
var settings = new ConsoleAppSettings(); | ||
var logger = this.CreateTestLogger<ConsoleAppContextFactory>(); | ||
var factory = new ConsoleAppContextFactory(appProcess, settings, logger); | ||
|
||
// Act | ||
var context = factory.CreateConsoleAppContext(args, commandParametersOption); | ||
|
||
// Assert | ||
var parameter = Assert.IsType<TestParameter>(context.Parameter); | ||
Assert.Equal(123, parameter.CategoryId); | ||
} | ||
|
||
[Fact] | ||
public void CreateConsoleAppContext_読み込んだアセンブリ内にCommandAttributeを付与したパラメーターがない_InvalidOperationExceptionが発生する() | ||
{ | ||
// Arrange | ||
var args = new string[] { "console-app-context-factory-test", "--category-id", "123" }; | ||
Type[] types = []; | ||
var assembly = new TestAssembly(types); | ||
Action<CommandParameterTypeCollection>? commandParametersOption = collection => collection.AddCommandParameterTypeFrom(assembly); | ||
var appProcess = new TestApplicationProcess(); | ||
var settings = new ConsoleAppSettings(); | ||
var logger = this.CreateTestLogger<ConsoleAppContextFactory>(); | ||
var factory = new ConsoleAppContextFactory(appProcess, settings, logger); | ||
|
||
// Act | ||
var action = () => factory.CreateConsoleAppContext(args, commandParametersOption); | ||
|
||
// Assert | ||
var ex = Assert.Throws<InvalidOperationException>(action); | ||
Assert.Equal("Maris.ConsoleApp.Core.CommandAttribute 属性を追加したコマンドパラメーターの型が読み込まれたアセンブリ TestAssembly に見つかりません。", ex.Message); | ||
} | ||
|
||
[Fact] | ||
public void CreateConsoleAppContext_起動パラメーターのパースに失敗する_DefaultValidationErrorExitCodeに設定した終了コードでプロセスが終了する() | ||
{ | ||
// Arrange | ||
var args = new string[] { "console-app-context-factory-test", "--category-id", "invalid-value" }; | ||
var types = new Type[] { typeof(TestParameter) }; | ||
var assembly = new TestAssembly(types); | ||
Action<CommandParameterTypeCollection>? commandParametersOption = collection => collection.AddCommandParameterTypeFrom(assembly); | ||
var appProcess = new TestApplicationProcess(); | ||
var exitCode = 789; | ||
var settings = new ConsoleAppSettings { DefaultValidationErrorExitCode = exitCode }; | ||
var logger = this.CreateTestLogger<ConsoleAppContextFactory>(); | ||
var factory = new ConsoleAppContextFactory(appProcess, settings, logger); | ||
|
||
// Act | ||
var action = () => factory.CreateConsoleAppContext(args, commandParametersOption); | ||
|
||
// Assert | ||
var ex = Assert.Throws<ApplicationException>(action); | ||
Assert.Equal("アプリケーションの終了処理が呼び出されました。", ex.Message); | ||
Assert.Equal(exitCode, appProcess.ExitCode); | ||
} | ||
|
||
private class TestAssembly : Assembly | ||
{ | ||
private readonly Type[] types; | ||
|
||
internal TestAssembly(Type[] types) => this.types = types; | ||
|
||
public override Type[] GetTypes() => this.types; | ||
|
||
public override AssemblyName GetName() => new(nameof(TestAssembly)); | ||
} | ||
|
||
[Command("console-app-context-factory-test", typeof(TestCommand))] | ||
private class TestParameter | ||
{ | ||
[Option("category-id", Required = true)] | ||
public long CategoryId { get; set; } | ||
} | ||
|
||
private class TestCommand : SyncCommand<TestParameter> | ||
{ | ||
protected internal override ICommandResult Execute(TestParameter parameter) | ||
=> new SuccessResult(); | ||
} | ||
|
||
private class TestApplicationProcess : IApplicationProcess | ||
{ | ||
public int ExitCode { get; private set; } | ||
|
||
[DoesNotReturn] | ||
public void Exit(int exitCode) | ||
{ | ||
this.ExitCode = exitCode; | ||
throw new ApplicationException("アプリケーションの終了処理が呼び出されました。"); | ||
} | ||
} | ||
} |
Oops, something went wrong.