简体   繁体   中英

Unit test that legacy code reacts to ThreadAbortException in a certain way

I've got an existing bit of legacy code that I want to get under test. Here's a repro of the essentials:

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;
    }
}

Now, this legacy unit has some issues, but for now I'm just trying to get it under test. Specifically, I want to test that collaborator.DoOtherStuff is not called if Response.End() raised a ThreadAbort .

The problem: how do you raise such an exception?

I've read through this question and its answers on ThreadAbortException , and understand that it's special. However, I don't see from those posts how to handle this in unit tests.

Here's my attempt:

[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);
}

Obviously the compiler complains: ThreadAbortException has no available constructor. Also, it's sealed (probably for good reasons), so creating a "testable" sub-class won't work.

What is the proper way to get such code under test? Is it even feasible, or is the LegacyUnit just too test-unfriendly?


Full, minimal repro (empty .NET 4.5 class library with NUnit 2.6.4 and Moq 4.5.9):

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);
    }
}

ThreadAbortException is raised in the target thread by calling Abort on it. You can create a thread to run the test and call Abort in your mock of testResponseMock.End eg

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM