1

このトピックについて別の質問に書きました。

ただし、コードをリファクタリングして構成アクセスを削除したため、仕様に合格できるようになりました。とか、そう思いました。TestDriven.Net を使用して、Visual Studio 内から正常に実行されます。ただし、rake 中に mspec.exe ツールを使用して実行すると、シリアライゼーション例外で失敗します。そのため、スレッドに偽のセキュリティ資格情報を設定する以外は基本的に何もしない、完全に自己完結型の例を作成しました。このテストは TD.Net では問題なくパスしますが、mspec.exe では失敗します。誰か提案はありますか?

更新: 回避策を発見しました。問題を調査した結果、プリンシパル オブジェクトを含むアセンブリが mspec.exe と同じフォルダーにないことが原因のようです。mspec が新しい AppDomain を作成して仕様を実行する場合、その新しい AppDomain は、逆シリアル化するためにプリンシパル オブジェクトを含むアセンブリを読み込む必要があります。そのアセンブリは mspec EXE と同じフォルダーにないため、失敗します。アセンブリを mspec と同じフォルダーにコピーすると、正常に動作します。

私がまだ理解していないのは、ReSharper と TD.Net がテストを問題なく実行できる理由です。実際にテストを実行するために mspec.exe を使用しませんか?

using System;
using System.Security.Principal;
using System.Threading;
using Machine.Specifications;

namespace MSpecTest
{
    [Subject(typeof(MyViewModel))]
    public class When_security_credentials_are_faked 
    {
        static MyViewModel SUT;

        Establish context = SetupFakeSecurityCredentials;

        Because of = () =>
            SUT = new MyViewModel();

        It should_be_initialized = () =>
            SUT.Initialized.ShouldBeTrue();

        static void SetupFakeSecurityCredentials()
        {
            Thread.CurrentPrincipal = CreatePrincipal(CreateIdentity());
        }

        static MyIdentity CreateIdentity()
        {
            return new MyIdentity(Environment.UserName, "None", true);
        }

        static MyPrincipal CreatePrincipal(MyIdentity identity)
        {
            return new MyPrincipal(identity);
        }
    }

    public class MyViewModel
    {
        public MyViewModel()
        {
            Initialized = true;
        }

        public bool Initialized { get; set; }
    }

    [Serializable]
    public class MyPrincipal : IPrincipal
    {
        private readonly MyIdentity _identity;

        public MyPrincipal(MyIdentity identity)
        {
            _identity = identity;
        }

        public bool IsInRole(string role)
        {
            return true;
        }

        public IIdentity Identity
        {
            get { return _identity; }
        }
    }

    [Serializable]
    public class MyIdentity : IIdentity
    {
        private readonly string _name;
        private readonly string _authenticationType;
        private readonly bool _isAuthenticated;

        public MyIdentity(string name, string authenticationType, bool isAuthenticated)
        {
            _name = name;
            _isAuthenticated = isAuthenticated;
            _authenticationType = authenticationType;
        }

        public string Name
        {
            get { return _name; }
        }

        public string AuthenticationType
        {
            get { return _authenticationType; }
        }

        public bool IsAuthenticated
        {
            get { return _isAuthenticated; }
        }
    }
}
4

1 に答える 1

5

ダン、

複製を提供していただきありがとうございます。

まず、コンソール ランナーは、TestDriven.NET および ReSharper ランナーとは異なる動作をします。基本的に、コンソール ランナーは、実行されるアセンブリごとに新しい AppDomain (および構成) を作成するという点で、さらに多くのセットアップ作業を実行する必要があります。これは、スペック アセンブリの .dll.config ファイルを読み込むために必要です。

スペック アセンブリごとに、次の 2 つの AppDomains が作成されます。

  1. 最初の AppDomain ( Console) は、mspec.exe の実行時に暗黙的に作成されます。
  2. 2 番目の AppDomain は、スペックを含むアセンブリ用に mspec.exe によって作成されます ( Spec)。

両方の AppDomain は .NET Remoting を介して相互に通信します。たとえば、仕様がAppDomain で実行されると、その事実が AppDomainSpecに通知されます。Console通知をConsole受け取ると、仕様情報をコンソールに書き込むことによって、それに応じて動作します。

Specとの間のこの通信は、 Console.NET Remoting によって透過的に実現されます。.NET Remoting の 1 つのプロパティはSpec、ターゲット AppDomain ( ) に通知を送信するときに、呼び出し元の AppDomain ( ) の一部のプロパティが自動的に含まれることConsoleです。Thread.CurrentPrincipalはそんな物件です。詳細については、こちらをご覧ください: http://sontek.vox.com/library/post/re-iprincipal-iidentity-ihttpmodule-serializable.html

Spec指定したコンテキストはAppDomainで実行されます。で設定Thread.CurrentPrincipalBecauseます。実行後、 AppDomainBecauseに通知が発行されます。Console通知にはMyPrincipal、受信側のConsoleAppDomain が逆シリアル化を試みるカスタムが含まれます。スペック アセンブリを認識していないため (プライベート ビン パスに含まれていないため)、これを行うことはできません。

これが、スペック アセンブリを mspec.exe と同じフォルダーに配置する必要があった理由です。

考えられる回避策は 2 つあります。

  1. プロキシを介してクロス AppDomain 通信に参加できるように (シリアル化される代わりに)派生MyPrincipalおよびMyIdentity派生します。MarshalByRefObject
  2. Thread.CurrentPrincipalで一時的に設定Because

(フォーマットを機能させるにはテキストが必要です -- 無視してください)

Because of = () => 
{
    var previousPrincipal = Thread.CurrentPrincipal;
    try
    {
        Thread.CurrentPrincipal = new MyPrincipal(...);
        SUT = new MyViewModel();
    }
    finally
    {
        Thread.CurrentPrincipal = previousPrincipal;
    }
}

たとえば、ReSharper はすべての通信作業を処理してくれます。MSpec の ReSharper Runner は、既存のインフラストラクチャにフックできます (これは、知る限り、.NET Remoting を使用していません)。

于 2010-02-28T16:35:32.840 に答える