How to write specs with MSpec for code that changes Thread.CurrentPrincipal?
- by Dan Jensen
I've been converting some old specs to MSpec (were using NUnit/SpecUnit). The specs are for a view model, and the view model in question does some custom security checking. We have a helper method in our specs which will setup fake security credentials for the Thread.CurrentPrincipal. This worked fine in the old unit tests, but fails in MSpec.
Specifically, I'm getting this exception:
"System.Runtime.Serialization.SerializationException: Type is not resolved for member"
It happens when part of the SUT tries to read the app config file. If I comment out the line which sets the CurrentPrincipal (or simply call it after the part that checks the config file), the error goes away, but the tests fail due to lack of credentials.
Similarly, if I set the CurrentPrincipal to null, the error goes away, but again the tests fail because the credentials aren't set. I've googled this, and found some posts about making sure the custom principal is serializable when it crosses AppDomain boundaries (usually in reference to web apps). In our case, this is not a web app, and I'm not crossing any AppDomains. Our pincipal object is also serializable.
I downloaded the source for MSpec, and found that the ConsoleRunner calls a class named AppDomainRunner. I haven't debugged into it, but it looks like it's running the specs in different app domains.
So does anyone have any ideas on how I can overcome this? I really like MSpec, and would love to use it exclusively. But I need to be able to supply fake security credentials while running the tests. Thanks!
Update: here's the spec class:
[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;
}
We have a number of these "picker" view models, all of which exhibit some common behavior. So I'm using the Behaviors feature of MSpec. This particular class is simulating the user selecting something from the (WPF) control which is bound to this VM.
The SetupFakeSecurityCredentials() method is simply setting Thread.CurrentPrincipal to an instance of our custom principal, where the prinipal has been populated will full-access rights.
Here's a fake CountryPickerViewModel which is enough to cause the error:
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; }
}
It's the ValidationFactory call which blows up. ValidationFactory is an Enterprise Library object, which tries to access the config.