如何使用 MSpec 为更改 Thread.CurrentPrincipal 的代码编写规范?

发布于 2024-08-20 19:48:09 字数 2332 浏览 8 评论 0原文

我一直在将一些旧规格转换为 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 的行为功能。这个特定的类正在模拟用户从绑定到该 VM 的 (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 是一个企业库对象,它尝试访问配置。

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文