現在の質問には多くの回答があり、コンソールc#アプリ、NUnitテスト-ループ-C#、およびおそらく他の多くのユニットテストへの最良の方法は、変更されていない「テスト不可能な」コンソールアプリケーションの直接ユニットテストを示していますテストするのに良い方法ではありません。それらはすべて正しいです。
ただし、何らかの理由で本当にこの方法でテストする必要があり、テストプロジェクトからの参照としてコンソールアプリケーションを参照できる場合(2つが同じソリューションにある場合は、おそらく可能です)、に頼ることなくそうすることが可能Process.Start
です。.NET 4.5以降では、xunit構文を使用します。
[Theory]
[MemberData("YourStaticDataProviderField")]
public async void SomeTest(string initialString, string resultString, params int[] indexes)
{
using (var consoleInStream = new AnonymousPipeServerStream(PipeDirection.Out))
using (var consoleOutStream = new AnonymousPipeServerStream(PipeDirection.In))
using (var writer = new StreamWriter(consoleInStream, Encoding.Default, 1024, true))
using (var reader = new StreamReader(consoleOutStream, Encoding.Default, false, 1024, true))
using (var tokenSource = new CancellationTokenSource())
{
// AutoFlush must be set to true to emulate actual console behavior,
// else calls to Console.In.Read*() may hang waiting for input.
writer.AutoFlush = true;
Task programTask = Task.Run(() =>
{
using (var consoleInReader =
new StreamReader(new AnonymousPipeClientStream(PipeDirection.In,
consoleInStream.GetClientHandleAsString())))
using (var consoleOutWriter =
new StreamWriter(new AnonymousPipeClientStream(PipeDirection.Out,
consoleOutStream.GetClientHandleAsString())))
{
// Again, AutoFlush must be true
consoleOutWriter.AutoFlush = true;
Console.SetIn(consoleInReader);
Console.SetOut(consoleOutWriter);
// Of course, pass any arguments your console application
// needs to run your test. Assuming no arguments are
// needed:
Program.Main(new string[0]);
}
}, tokenSource.Token);
// Read and write as your test dictates.
await writer.WriteLineAsync(initialString.Length.ToString());
await writer.WriteLineAsync(initialString);
await writer.WriteLineAsync(indexes.Length.ToString());
await writer.WriteLineAsync(String.Join(" ", indexes));
var result = await reader.ReadLineAsync();
await writer.WriteLineAsync();
// It is probably a good idea to set a timeout in case
// the method under test does not behave as expected (e.g.,
// is still waiting for input). Adjust 5000 milliseconds
// to your liking.
if (!programTask.Wait(5000, tokenSource.Token))
{
tokenSource.Cancel();
Assert.False(true, "programTask did not complete");
}
// Assert whatever your test requires.
Assert.Null(programTask.Exception);
Assert.Equal(resultString, result);
}
}
このソリューションは、非同期メソッドの処理方法が異なる場合、.NET3.5以降に適応できる可能性があります。 AnonymousPipe(Server|Client)Stream
.NET3.5で導入されました。他の単体テストフレームワークは、適切な構文変更で動作するはずです。
パイプストリームSystem.IO.Pipes.AnonymousPipeServerStream
とSystem.IO.Pipes.AnonymousPipeClientStream
は、このソリューションを機能させるための鍵です。ストリームには現在の位置があるため、2つの異なるプロセスが同時に同じものを参照することはそれほど確実には機能しませんMemoryStream
。代わりにパイプストリームを使用すると、ここで行うように、親プロセスと子プロセスでストリームを使用できます。プログラムの実行Program.Main(string[])
中に単体テストプロセスがコンソールから読み取りおよび書き込みを行えるようにするには、子タスクで実行する必要があります。AnonymousPipeClientStream
ドキュメントによると、オブジェクトは子タスクに属している必要があります。そのため、オブジェクトはタスクランナー内で作成されます。
programTask
例外をテストする必要がある場合は、オブジェクトから例外データを取得できます(または、xunitの下でAssert.ThrowsAsync<ExpectedException>(Func<Task>)
、子タスクを実行するようなものを使用します)。