2016-05-20 9 views
4

JavaのExpectedSystemExitに相当する何らかのC#がありますか?コード内にexitがあり、実際にテストできるようにしたいと考えています。私がC#で見つけた唯一の事は、本当に素敵ではないworkaroundです。Test Environment.Exit()in C#

サンプルコード

public void CheckRights() 
{ 
    if(!service.UserHasRights()) 
    { 
     Environment.Exit(1); 
    } 
} 

テストコード

[TestMethod] 
public void TestCheckRightsWithoutRights() 
{ 
    MyService service = ... 
    service.UserHasRights().Returns(false); 

    ??? 
} 

私は(あざけるため+ NSubstitute)をテストするためのVSフレームワークを使用していますが、NUnitのに切り替える問題ではありませんまたはこのテストのために何でも。

+1

? – Gimly

+0

この終了を規則正しい方法で行ったサービスを作成し、サービスに置き換えることができます。私はあなたが 'Environment.Exit'が動作することを実際にテストしていないと仮定していますが、それは単に呼び出されたものですか? –

+2

あなたはHeavenを経験したことがありますか.Exit()は機能しませんでしたか?その突風はうまくいく。 – csa

答えて

0

私は私のテストで模擬することができた新しいメソッドを作成しました。

コード

public void CheckRights() 
{ 
    if(!service.UserHasRights()) 
    { 
     Environment.Exit(1); 
    } 
} 

internal virtual void Exit() 
{ 
    Environment.Exit(1); 
} 

ユニットテストを使用しているユニットテストフレームワーク

[TestMethod] 
public void TestCheckRightsWithoutRights() 
{ 
    MyService service = ... 
    service.When(svc => svc.Exit()).DoNotCallBase(); 
    ... 
    service.CheckRights(); 
    service.Received(1).Exit(); 
} 
9

環境出口を提供するインタフェースをテスト対象のクラスに提供するには、依存関係注入を使用する必要があります。例えば

public interface IEnvironment 
{ 
    void Exit(int code); 
} 

さんはまた、あなたがUserHasRights()を呼び出すためのインタフェースを持っていると仮定しましょう:

public sealed class RightsChecker 
{ 
    readonly IRightsService service; 
    readonly IEnvironment environment; 

    public RightsChecker(IRightsService service, IEnvironment environment) 
    { 
     this.service  = service; 
     this.environment = environment; 
    } 

    public void CheckRights() 
    { 
     if (!service.UserHasRights()) 
     { 
      environment.Exit(1); 
     } 
    } 
} 

public interface IRightsService 
{ 
    bool UserHasRights(); 
} 

さて、このようになりますあなたのクラスをテストすると仮定

これで、mockingフレームワークを使用して、IEnvironment .Exit()がundそれは正しい条件です。

[TestMethod] 
public static void CheckRights_exits_program_when_user_has_no_rights() 
{ 
    var rightsService = new Mock<IRightsService>(); 
    rightsService.Setup(foo => foo.UserHasRights()).Returns(false); 

    var enviromnent = new Mock<IEnvironment>(); 

    var rightsChecker = new RightsChecker(rightsService.Object, enviromnent.Object); 

    rightsChecker.CheckRights(); 

    enviromnent.Verify(foo => foo.Exit(1)); 
} 

周囲コンテキストとクロス切断

Environment.Exit()が横断的であると考えられるような方法に関する:例えば、このようなビットに見えるかもしれないMoqを使用して追加のコンストラクタパラメータが爆発的に増加する可能性があるため、インタフェースを回避することをお勧めします。 (注:クロスカット問題の標準例はDateTime.Nowです)

この問題に対処するには、「アンビエントコンテキスト」を導入することができます。それをテストします。もちろん、そのようなことは控えめに、そして真のクロスカッティングの懸念に対してのみ使用されるべきです。

たとえば、あなたはとても似Environment周囲のコンテキストを導入する可能性:

public abstract class EnvironmentControl 
{ 
    public static EnvironmentControl Current 
    { 
     get 
     { 
      return _current; 
     } 

     set 
     { 
      if (value == null) 
       throw new ArgumentNullException(nameof(value)); 

      _current = value; 
     } 
    } 

    public abstract void Exit(int value); 

    public static void ResetToDefault() 
    { 
     _current = DefaultEnvironmentControl.Instance; 
    } 

    static EnvironmentControl _current = DefaultEnvironmentControl.Instance; 
} 

public class DefaultEnvironmentControl : EnvironmentControl 
{ 
    public override void Exit(int value) 
    { 
     Environment.Exit(value); 
    } 

    public static DefaultEnvironmentControl Instance => _instance.Value; 

    static readonly Lazy<DefaultEnvironmentControl> _instance = new Lazy<DefaultEnvironmentControl>(() => new DefaultEnvironmentControl()); 
} 

通常のコードは、ちょうどEnvironmentControl.Current.Exit()を呼び出します。この変更により、IEnvironmentパラメータがRightsCheckerクラスから消える:

public sealed class RightsChecker 
{ 
    readonly IRightsService service; 

    public RightsChecker(IRightsService service) 
    { 
     this.service = service; 
    } 

    public void CheckRights() 
    { 
     if (!service.UserHasRights()) 
     { 
      EnvironmentControl.Current.Exit(1); 
     } 
    } 
} 

しかし、我々はまだそれが呼び出されていることをユニットテストする能力を保持する:

public static void CheckRights_exits_program_when_user_has_no_rights() 
{ 
    var rightsService = new Mock<IRightsService>(); 
    rightsService.Setup(foo => foo.UserHasRights()).Returns(false); 

    var enviromnent = new Mock<EnvironmentControl>(); 
    EnvironmentControl.Current = enviromnent.Object; 

    try 
    { 
     var rightsChecker = new RightsChecker(rightsService.Object); 
     rightsChecker.CheckRights(); 
     enviromnent.Verify(foo => foo.Exit(1)); 
    } 

    finally 
    { 
     EnvironmentControl.ResetToDefault(); 
    } 
} 

For more information about ambient contexts, see here.

+0

ありがとうございます...しかし、それは私が単に好きではない同じ回避策です。私のテストでは、私の生産的なコードで追加のクラス/インターフェース/メソッドを作成せずに使用できるJUnitルールに相当するものはありませんか? – Antiohia

+1

@Antiohiaこのようなクロスカッティングの問題に対する共通の解決策を追加します。これは少なくとも、追加のインターフェイスを渡す必要はないということです。 C#でクラスを追加しなくてもこのようなことはできませんが、実際のクロスカッティングの問題(セキュリティ、環境管理、現在の日付時刻など)では、周囲のコンテキストを導入することで影響を最小限に抑えることができます。 –

+0

ありがとうございました!私はそれについて考えるだろう。 – Antiohia

0

あなたの場合は目標は、テストをサポートするためだけの余分なクラスやインターフェースを避けることです。プロパティインジェクションを介してEnvironment.Exitアクションについてどのように感じますか?

class RightsChecker 
{ 
    public Action AccessDeniedAction { get; set; } 

    public RightsChecker(...) 
    { 
     ... 
     AccessDeniedAction =() => Environment.Exit(); 
    } 
} 

[Test] 
public TestCheckRightsWithoutRights() 
{ 
    ... 
    bool wasAccessDeniedActionExecuted = false; 
    rightsChecker.AccessDeniedAction =() => { wasAccessDeniedActionExecuted = true; } 
    ... 
    Assert.That(wasAccessDeniedActionExecuted , Is.True); 
}