Spec fails when run by mspec.exe, but passes when run by TD.NET

一个人想着一个人 提交于 2019-11-29 16:33:57

Dan,

thank you for providing a reproduction.

First off, the console runner works differently than the TestDriven.NET and ReSharper runners. Basically, the console runner has to perform a lot more setup work in that it creates a new AppDomain (plus configuration) for every assembly that is run. This is required to load the .dll.config file for your spec assembly.

Per spec assembly, two AppDomains are created:

  1. The first AppDomain (Console) is created implicitly when mspec.exe is executed,
  2. a second AppDomain is created by mspec.exe for the assembly containing the specs (Spec).

Both AppDomains communicate with each other through .NET Remoting: For example, when a spec is executed in the Spec AppDomain, it notifies the Console AppDomain of that fact. When Console receives the notification it acts accordingly by writing the spec information to the console.

This communiciation between Spec and Console is realized transparently through .NET Remoting. One property of .NET Remoting is that some properties of the calling AppDomain (Spec) are automatically included when sending notifications to the target AppDomain (Console). Thread.CurrentPrincipal is such a property. You can read more about that here: http://sontek.vox.com/library/post/re-iprincipal-iidentity-ihttpmodule-serializable.html

The context you provide will run in the Spec AppDomain. You set Thread.CurrentPrincipal in the Because. After Because ran, a notification will be issued to the Console AppDomain. The notification will include your custom MyPrincipal that the receiving Console AppDomain tries to deserialize. It cannot do that since it doesn't know about your spec assembly (as it is not included in its private bin path).

This is why you had to put your spec assembly in the same folder as mspec.exe.

There are two possible workarounds:

  1. Derive MyPrincipal and MyIdentity from MarshalByRefObject so that they can take part in cross-AppDomain communication through a proxy (instead of being serialized)
  2. Set Thread.CurrentPrincipal transiently in the Because

(Text is required for formatting to work -- please ignore)

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

ReSharper, for example, handles all the communication work for us. MSpec's ReSharper Runner can hook into the existing infrastructure (that, AFAIK, does not use .NET Remoting).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!