0
votes

We're using XUnit 2.1.0 from NuGet with the relating runners, console and visualstudio as documented here under the "running tests with visual studio" heading and relating content.

I'm also using Visual Studio 2015 Enterprise Update 2. The only thing that's reasonably out of date is CSLA, we're on 4.0.1 (I think 5 years old?)

When we run any tests that require a DataPortal fetch, the test falls over as soon as the DataPortal fetch attempts to be sent to the server. WCF throws a "System.ServiceModel.FaultException" stating "Invalid token for impersonation - it cannot be duplicated." It's important to note none of the tests attempt to impersonate another user. the falling over happens in any test which attempts to use CSLA to do a DataPortal call. We've recently moved over from xunit 1.x to 2.x through nuget, we used to run xunit from the xunit runner when testing our tests locally but that is now deprecated. The tests all ran absolutely fine through both the Gui and console runner for xunit 1.x. Now we have to use the visual studio runner with xunit 2.x we are getting this crazy exception.

edit: if you run xunit 2.x console runner from outside of visual studio the tests are also fine on 2.x, it's the visual studio side of things that is not working.

Stack trace below:

Server stack trace: 
   at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at Csla.Server.Hosts.IWcfPortal.Fetch(FetchRequest request)
   at Csla.DataPortalClient.WcfProxy.Fetch(Type objectType, Object criteria, DataPortalContext context) in D:\Dev\Insight\Trunk\Source\Lib\CSLA .NET\4.0\Source\Csla\DataPortalClient\WcfProxy.cs:line 162
   at Csla.DataPortal.Fetch(Type objectType, Object criteria) in D:\Dev\Insight\Trunk\Source\Lib\CSLA .NET\4.0\Source\Csla\DataPortal.cs:line 245
   at Csla.DataPortal.Fetch[T](Object criteria) in D:\Dev\Insight\Trunk\Source\Lib\CSLA .NET\4.0\Source\Csla\DataPortal.cs:line 170

Again this works fine if we run the tests from another test runner, either the old xunit test runner or CruiseControl.Net for example (We use CC.Net for continuous integration and this runs the tests fine)

1

1 Answers

1
votes

I think this is more of an issue with the way that the Visual Studio test runner sets up the current user principal. Most of the other test runners appear to use an empty GenericPrincipal, whereas the VS one appears to set the current principal to an impersonated version of the current windows identity. This means that you get the error you are seeing when CSLA.NET attempts to impersonate it again.

The issue is discussed in detail in this blog post relating to NUnit: http://www.ienumerable.it/2015/03/21/Setting-up-good-fixture.html

A relatively simple way to solve this with xUnit (adapted from the blog above) is to set up a BeforeAfterTestAttribute to set it to a GenericPrincipal before the test runs and then restore the original principal afterwards. This guarantees that it will run with the same principal, regardless of the test runner used.

public class RequiresGenericPrincipalAttribute : BeforeAfterTestAttribute
{
    private IPrincipal _originalPrincipal;
    public override void Before(MethodInfo methodUnderTest)
    {
        _originalPrincipal = System.Threading.Thread.CurrentPrincipal;
        System.Threading.Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(""), new String[] { });
        base.Before(methodUnderTest);                        
    }

    public override void After(MethodInfo methodUnderTest)
    {
        base.After(methodUnderTest);
        System.Threading.Thread.CurrentPrincipal = _originalPrincipal;
    }

}