3

テストしたい既存のレガシーコードがあります。要点の再現は次のとおりです。

public class LegacyUnit
{
    private readonly ICollaborator collaborator;

    public LegacyUnit(ICollaborator collaborator) 
    {
        this.collaborator = collaborator;
    }

    public object GetStuff(HttpContextBase context, string input)
    {
        try 
        {
            if (input == "")
            {
                context.Response.End();
            }

            collaborator.DoOtherStuff();

            return "Done!";
        }
        catch (ThreadAbortException) 
        { }

        return null;
    }
}

現在、このレガシー ユニットにはいくつかの問題がありますが、今のところはテストを行っているところです。具体的には、 を上げた場合に呼び出されcollaborator.DoOtherStuffないことをテストしたい。Response.End()ThreadAbort

問題: どうやってそのような例外を発生させるのですか?

でこの質問とその回答をThreadAbortException読み、それが特別であることを理解しました。ただし、これらの投稿から、単体テストでこれを処理する方法がわかりません。

これが私の試みです:

[Test]
public void DoesNotCallCollaboratorOnThreadAbort()
{
    var testResponseMock = new Mock<HttpResponseBase>();
    var testContextMock = new Mock<HttpContextBase>();
    var collaboratorMock = new Mock<ICollaborator>();

    testContextMock.Setup(x => x.Response).Returns(testResponseMock.Object);
    testResponseMock.Setup(x => x.End()).Throws<ThreadAbortException>(); // Compile error

    var unit = new LegacyUnit(collaboratorMock.Object);
    unit.GetStuff(testContextMock.Object, "");

    collaboratorMock.Verify(c => c.DoOtherStuff(), Times.Never);
}

明らかに、コンパイラーは文句を言います:ThreadAbortException利用可能なコンストラクターがありません。また、sealed(おそらく正当な理由で) そのため、「テスト可能な」サブクラスの作成は機能しません。

そのようなコードをテストする適切な方法は何ですか? それは実現可能ですか、それともLegacyUnitあまりにもテストに不向きですか?


完全で最小限の再現 (NUnit 2.6.4 および Moq 4.5.9 を使用した空の .NET 4.5 クラス ライブラリ):

public interface ICollaborator
{
    void DoOtherStuff();
}

public class LegacyUnit
{
    private readonly ICollaborator collaborator;

    public LegacyUnit(ICollaborator collaborator)
    {
        this.collaborator = collaborator;
    }

    public object GetStuff(HttpContextBase context, string input)
    {
        try
        {
            if (input == "") context.Response.End();
            collaborator.DoOtherStuff();
            return "Done!";
        }
        catch (ThreadAbortException)
        { }

        return null;
    }
}

[TestFixture]
public class LegacyUnitTests
{
    [Test]
    public void DoesNotCallCollaboratorOnThreadAbort()
    {
        var testResponseMock = new Mock<HttpResponseBase>();
        var testContextMock = new Mock<HttpContextBase>();
        var collaboratorMock = new Mock<ICollaborator>();

        testContextMock.Setup(x => x.Response).Returns(testResponseMock.Object);
        testResponseMock.Setup(x => x.End()).Throws<ThreadAbortException>(); // Compile error here

        var unit = new LegacyUnit(collaboratorMock.Object);
        unit.GetStuff(testContextMock.Object, "");

        collaboratorMock.Verify(c => c.DoOtherStuff(), Times.Never);
    }
}
4

1 に答える 1

6

ThreadAbortExceptionそれを呼び出すAbortことによってターゲットスレッドで発生します。テストを実行するスレッドを作成し、たとえばAbortのモックを呼び出すことができますtestResponseMock.End

testContextMock.Setup(x => x.Response).Returns(testResponseMock.Object);

var unit = new LegacyUnit(collaboratorMock.Object);
var thread = new Thread(() => unit.GetStuff(testContextMock.Object, ""));

testResponseMock.Setup(x => x.End()).Callback(() => { Thread.CurrentThread.Abort(); });

thread.Start();
thread.Join();

collaboratorMock.Verify(c => c.DoOtherStuff(), Times.Never);
于 2016-06-15T10:07:41.140 に答える