Search code examples
wcfunit-testingmstestgallioxunit

.NET unit test runner outputting FaultException.Detail


I am running some unit tests on a WCF service. The service is configured to include exception details in the fault response (with the following in my service configuration file).

<serviceDebug includeExceptionDetailInFaults="true" />

If a test causes an unhandled exception on the server the fault is received by the client with a fully populated server stack trace. I can see this by calling the exception's ToString() method. The problem is that this doesn't seem to be output by any of the test runners that I have tried (xUnit, Gallio, MSTest). They appear to just output the Message and the StackTrace properties of the exception.

To illustrate what I mean, the following unit test will output three sections:

  • Error Message
  • Error Stack Trace
  • Standard Console Output (contains the information I would like, e.g. "Fault Detail is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is: ..."

public void Test()
{
    try
    {
        service.CallMethodWhichCausesException();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex); // this outputs the information I would like
       throw;
    }
}

Edit: However, I would prefer that I was not forced to catch the exception in each test and write it to the console in order to ascertain the content within a FaultException object's Detail property.

For example,

public void Test()
{
    service.CallMethodWhichCausesException();
}

Having this information will make the initial phase of testing and deployment a lot less painful.

I know I can just wrap each unit test in a generic exception handler and write the exception to the console and rethrow (as above) within all my unit tests but that seems a very long-winded way of achieving this (and would look pretty awful).

Does anyone know if there's any way to get this information included whenever an unhandled exception occurs? Is there a setting that I am missing? Is my service configuration lacking in proper fault handling? Perhaps I could write some kind of plug-in / adapter for some unit testing framework? Perhaps theres a different unit testing framework which I should be using instead!

My actual set-up is xUnit unit tests executed via Gallio for the development environment, but I do have a separate suite of "smoke tests" written which I would like to be able to have our engineers run via the xUnit GUI test runner (or Gallio or whatever) to simplify the final deployment.

Thanks.

Adam


Solution

  • I think I have found a solution. Oleg Sych has implemented a WCF behaviour which transparently marshals exceptions from the server to client, raising them on the client as if they had occurred there.

    It's as simple as adding a service behaviour attribute to the service contract (interface).

    This approach is especially beneficial if it is decided that existing code, written to execute within a single process, should distributed via WCF services. It means that it can be achieved without requiring any changes to the client's exception handling (e.g. you can still handle a SecurityException on the client instead of having to handle a FaultException<SecurityException> when you move to a more distributed design).

    The post can be found here: http://www.olegsych.com/2008/07/simplifying-wcf-using-exceptions-as-faults/

    Since my WCF services are using a mixture of ws2007HttpBinding the basicHttpBinding (for interoperability), I had to make a few changes to the code (as mentioned in the comments of the post above). Specifically:

    In ExceptionMarshallingMessageInspector.AfterReceiveReply:

    Exception exception = faultDetail as Exception;
    if (exception != null)
    {
        // NB: Error checking etc. excluded
        // Get the _remoteStackTraceString of the Exception class
    
        FieldInfo remoteStackTraceString = typeof(Exception).GetField(
            "_remoteStackTraceString",
            BindingFlags.Instance | BindingFlags.NonPublic);
    
        // Set the InnerException._remoteStackTraceString to the current InnerException.StackTrace
        remoteStackTraceString.SetValue(
            exception,
            exception.StackTrace + Environment.NewLine);
    
        throw exception;
    }
    

    ...and in ExceptionMarshallingMessageInspector.ReadFaultDetail I made a change so that it looks for a detail node with a local name equal to either "Detail" (for ws2007HttpBinding" or "detail" (for basicHttpBinding).

    I hope this helps.

    Adam