Как написать спецификации с MSpec для кода, который изменяет Thread.CurrentPrincipal?
Я конвертировал некоторые старые спецификации в MSpec (использовал NUnit/SpecUnit). Спецификации предназначены для модели представления, и рассматриваемая модель представления выполняет некоторую пользовательскую проверку безопасности. У нас есть вспомогательный метод в наших спецификациях, который настроит поддельные учетные данные безопасности для Thread.CurrentPrincipal. Это хорошо работало в старых модульных тестах, но не работает в MSpec. В частности, я получаю это исключение:
"System.Runtime.Serialization.SerializationException: тип не разрешен для члена"
Это происходит, когда часть SUT пытается прочитать файл конфигурации приложения. Если я закомментирую строку, которая устанавливает CurrentPrincipal (или просто вызову его после части, которая проверяет файл конфигурации), ошибка исчезнет, но тесты не пройдут из-за отсутствия учетных данных.
Точно так же, если я установлю для CurrentPrincipal значение null, ошибка исчезнет, но снова тесты не пройдутся, поскольку учетные данные не установлены. Я погуглил это и нашел несколько сообщений о том, что сериал можно настраивать, когда пользователь пересекает границы AppDomain (обычно это касается веб-приложений). В нашем случае это не веб-приложение, и я не пересекаю никакие домены приложений. Наш главный объект также сериализуем.
Я загрузил исходный код MSpec и обнаружил, что ConsoleRunner вызывает класс с именем AppDomainRunner. Я не отлаживал его, но похоже, что он запускает спецификации в разных доменах приложений.
Так у кого-нибудь есть идеи, как мне это преодолеть? Мне очень нравится MSpec, и я хотел бы использовать его исключительно. Но я должен быть в состоянии предоставить поддельные учетные данные безопасности во время выполнения тестов.
Вот класс спецификаций:
[Subject(typeof(CountryPickerViewModel))]
public class When_the_user_makes_a_selection : PickerViewModelSpecsBase
{
protected static CountryPickerViewModel picker;
Establish context = () =>
{
SetupFakeSecurityCredentials();
CreateFactoryStubs();
StubLookupServicer<ICountryLookupServicer>()
.WithData(BuildActiveItems(new [] { "USA", "UK" }));
picker = new CountryPickerViewModel(ViewFactory, ViewModelFactory,
BusinessLogicFactory, CacheFactory);
};
Because of = () =>
picker.SelectedItem = picker.Items[0];
Behaves_like<Picker_that_has_a_selected_item> a_picker_with_a_selection;
}
У нас есть несколько таких моделей представления "сборщик", каждая из которых демонстрирует некоторое общее поведение. Поэтому я использую функцию поведения в MSpec. Этот конкретный класс имитирует выбор пользователем чего-либо из элемента управления (WPF), который связан с этой виртуальной машиной. Метод SetupFakeSecurityCredentials() просто устанавливает для Thread.CurrentPrincipal экземпляр нашего пользовательского принципала, в котором заполненный принципал будет иметь права полного доступа.
Вот поддельная CountryPickerViewModel, которой достаточно, чтобы вызвать ошибку:
public class CountryPickerViewModel
{
public CountryPickerViewModel(IViewFactory viewFactory,
IViewModelFactory viewModelFactory,
ICoreBusinessLogicFactory businessLogicFactory,
ICacheFactory cacheFactory)
{
Items = new Collection<int>();
var validator = ValidationFactory.CreateValidator<object>();
}
public int SelectedItem { get; set; }
public Collection<int> Items { get; private set; }
}
Это взрыв ValidationFactory, который взрывается. ValidationFactory - это объект Enterprise Library, который пытается получить доступ к конфигурации.