2016-06-15 3 views
3

私はテスト中のレガシーコードを手に入れました。ここでは必需品のREPROです:レガシーコードが特定の方法でThreadAbortExceptionに反応するというユニットテスト

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

、このレガシーユニットは、いくつかの問題がありますが、今の私は、テストの下でそれを取得しようとしています。具体的には、がThreadAbortの場合collaborator.DoOtherStuffではなく、であることをテストしたいと考えています。

問題:このような例外はどのように発生させますか?

私は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です(おそらく理由があるため)、 "テスト可能な"サブクラスの作成は機能しません。

テスト中にこのようなコードを取得する適切な方法は何ですか?それは実現可能なのでしょうか、またはLegacyUnitもあまりにもテストに不満ですか?


フル、最小限のREPRO(NUnitの2.6.4および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); 
    } 
} 
+0

End()が任意のタイプの例外をスローすると、DoOtherStuffも実行されません。 – Jehof

+0

真。私の実際のケースでは、他の種類の例外を巡って、いろいろなことが起こっています。可能であれば最小限のレプロを行うには、完全性を犠牲にしなければならなかった。 – Jeroen

答えて

5

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

素晴らしいですが、うまく機能しているようです。一つのこと:Moqの私のバージョンでは '.Do(...)'を '.Callback(...) 'に置き換えなければならなかった。その矛盾がどこから来るのか分かりますか? – Jeroen

+1

@Jeroen - 申し訳ありませんが、私は 'moq'タグに気付かず、あなたがサイコロを使っていると思っていました。私は答えを更新しました。 – Lee

関連する問題