遗留代码以某种方式对 ThreadAbortException 做出反应的单元测试
Unit test that legacy code reacts to ThreadAbortException in a certain way
我有一些现有的遗留代码,我想对其进行测试。这是要点的复制品:
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
.
问题:如何引发这样的异常?
我已通读this question and its answers on 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
(可能有充分的理由),因此创建 "testable" sub-class 将不起作用。
获取此类待测代码的正确方法是什么?它是否可行,或者 LegacyUnit
是否太不适合测试?
完整的最小重现(带有 NUnit 2.6.4 和 Moq 4.5.9 的空 .NET 4.5 class 库):
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
通过调用 Abort
在目标线程中引发。您可以创建一个线程来 运行 测试并在 testResponseMock.End
的模拟中调用 Abort
例如
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);
我有一些现有的遗留代码,我想对其进行测试。这是要点的复制品:
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
.
问题:如何引发这样的异常?
我已通读this question and its answers on 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
(可能有充分的理由),因此创建 "testable" sub-class 将不起作用。
获取此类待测代码的正确方法是什么?它是否可行,或者 LegacyUnit
是否太不适合测试?
完整的最小重现(带有 NUnit 2.6.4 和 Moq 4.5.9 的空 .NET 4.5 class 库):
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
通过调用 Abort
在目标线程中引发。您可以创建一个线程来 运行 测试并在 testResponseMock.End
的模拟中调用 Abort
例如
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);