3

Visual Studioにテストプロジェクトがあり、コンソールアプリケーションを(同じソリューションで)テストするために使用したいと思います。

特定のパラメーターを使用してコンソールアプリを呼び出すテストを設定し、実際の出力を期待するものと比較してから、通常のAssertステートメントを実行してテストに適切に合格/不合格にしようとしています。

これを行うための最良の方法は、私が思いつくことができますが、単体テスト内でSystem.Diagnostics.Processを使用してappexeを実行することです。これは機能します。出力を読み取ることができ、すべてが順調です。

私が抱えている問題は、コンソールアプリのコード内にブレークポイントを設定して、デバッグを実行できるようにする場合です。Processがコンソールアプリを開始したため、Visual Studioはコンソールアプリを監視していないため、中断することはありません。Web Appsのように「外部アプリケーションからのリクエストを待つ」のようなものはなく、その理由は理解できますが、基本的にはそれが私が探しているものです。

だから私の質問は、コンソールアプリをデバッグできるVisualStudio内でこれらの単体テストを設定する方法はありますか?私が考えることができる唯一の回避策は、コンソールアプリケーションで開始アクションを設定して、MSTest.exeを呼び出す外部プログラムを開始し、その方法で適切な単体テストを実行することです。しかし、これは私が間違っていると思っている問題のように思えます。実際には、はるかに明白な解決策があります。

4

3 に答える 3

10

コンソールアプリケーションを可能な限り薄くし、すべてのビジネスロジックをドメインクラスに移動します。例えば

class Program
{
    static void Main(string[] args)
    {
       Foo foo = new Foo(args);
    }
}

その後、Fooクラスの単体テストを簡単に作成できます。

于 2012-05-03T00:19:20.810 に答える
1

ユニットテストは人間の相互作用を必要とすべきではありません。アプリケーションをユニットテスト可能にするには、コンソールのTextReader相互作用を抽象化する必要があります。これは、TextWriterクラスを使用して簡単に実行できます。この質問が役立つかもしれません。

于 2012-05-03T00:11:32.283 に答える
1

現在の質問には多くの回答があり、コンソール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.AnonymousPipeServerStreamSystem.IO.Pipes.AnonymousPipeClientStreamは、このソリューションを機能させるための鍵です。ストリームには現在の位置があるため、2つの異なるプロセスが同時に同じものを参照することはそれほど確実には機能しませんMemoryStream。代わりにパイプストリームを使用すると、ここで行うように、親プロセスと子プロセスでストリームを使用できます。プログラムの実行Program.Main(string[])中に単体テストプロセスがコンソールから読み取りおよび書き込みを行えるようにするには、子タスクで実行する必要があります。AnonymousPipeClientStreamドキュメントによると、オブジェクトは子タスクに属している必要があります。そのため、オブジェクトはタスクランナー内で作成されます。

programTask例外をテストする必要がある場合は、オブジェクトから例外データを取得できます(または、xunitの下でAssert.ThrowsAsync<ExpectedException>(Func<Task>)、子タスクを実行するようなものを使用します)。

于 2015-09-11T18:10:22.133 に答える