Spec не запускается при запуске mspec.exe, но проходит при запуске TD.NET

Я писал об этой теме в другом вопросе.

Тем не менее, с тех пор я реорганизовал свой код, чтобы избавиться от доступа к конфигурации, что позволило пройти спецификации. Или я так думал. Они отлично работают в Visual Studio, используя TestDriven.Net. Тем не менее, когда я запускаю их во время рейка с помощью средства mspec.exe, они все равно завершаются с исключением сериализации. Итак, я создал полностью автономный пример, который практически ничего не делает, кроме установки поддельных учетных данных безопасности в потоке. Этот тест проходит нормально в TD.Net, но взрывается в mspec.exe. У кого-нибудь есть предложения?

Обновление: я обнаружил обходной путь. После исследования проблемы, кажется, причина в том, что сборка, содержащая мой основной объект, не находится в той же папке, что и mspec.exe. Когда mspec создает новый AppDomain для запуска моих спецификаций, этот новый AppDomain должен загрузить сборку с основным объектом, чтобы десериализовать его. Эта сборка не находится в той же папке, что и EXE-файл mspec, поэтому происходит сбой. Если я скопировал мою сборку в ту же папку, что и 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; }
        }
    }
}

1 ответ

Дэн,

спасибо за предоставленную репродукцию.

Прежде всего, консольный бегун работает иначе, чем бегуны TestDriven.NET и ReSharper. По сути, консольный исполнитель должен выполнить гораздо больше работы по настройке, поскольку он создает новый домен приложений (плюс конфигурация) для каждой выполняемой сборки. Это необходимо для загрузки файла.dll.config для вашей сборки спецификации.

В соответствии со спецификацией сборки создаются два домена приложений:

  1. Первый AppDomain (Console) создается неявно при запуске mspec.exe,
  2. mspec.exe для сборки, содержащей спецификации, создается второй домен приложения. Spec).

Оба домена приложений взаимодействуют друг с другом через.NET Remoting: например, когда спецификация выполняется в Spec AppDomain, он уведомляет Console AppDomain этого факта. когда Console Получив уведомление, он действует соответствующим образом, записав информацию о спецификации в консоль.

Это общение между Spec а также Console прозрачно реализовано через.NET Remoting. Одним из свойств.NET Remoting является то, что некоторые свойства вызывающего AppDomain (Spec) автоматически включаются при отправке уведомлений целевому домену приложений (Console). Thread.CurrentPrincipal это такая собственность. Вы можете прочитать больше об этом здесь: http://sontek.vox.com/library/post/re-iprincipal-iidentity-ihttpmodule-serializable.html

Предоставленный вами контекст будет работать в Spec AppDomain. Ты устанавливаешь Thread.CurrentPrincipal в Because, После Because побежал, уведомление будет выдано Console AppDomain. Уведомление будет включать ваш заказ MyPrincipal что получение Console AppDomain пытается десериализовать. Он не может этого сделать, поскольку не знает о вашей сборке спецификаций (так как он не включен в путь к своему приватному бину).

Вот почему вы должны были поместить вашу спецификацию сборки в ту же папку, что и mspec.exe.

Есть два возможных обходных пути:

  1. получать 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, например, обрабатывает всю коммуникационную работу за нас. Repecharper Runner от MSpec может подключиться к существующей инфраструктуре (которая, AFAIK, не использует.NET Remoting).

Другие вопросы по тегам