環境出口を提供するインタフェースをテスト対象のクラスに提供するには、依存関係注入を使用する必要があります。例えば
:
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.
? – Gimly
この終了を規則正しい方法で行ったサービスを作成し、サービスに置き換えることができます。私はあなたが 'Environment.Exit'が動作することを実際にテストしていないと仮定していますが、それは単に呼び出されたものですか? –
あなたはHeavenを経験したことがありますか.Exit()は機能しませんでしたか?その突風はうまくいく。 – csa