Search code examples
c#unit-testingexceptionftpwebresponse

How can I create a WebException with an FtpWebResponse for testing?


I'm using FtpWebRequest and a Transient Fault Handling application block. For my fault handler, I have an error detection strategy that checks if the response is considered transient so that it knows whether or not to retry:

public bool IsTransient(Exception ex)
    {

        var isTransient = false;
        //will be false if the exception is not a web exception.
        var webEx = ex as WebException;

        //happens when receiving a protocol error.
        //This protocol error wraps the inner exception, e.g. a 401 access denied.
        if (webEx != null && webEx.Status == WebExceptionStatus.ProtocolError)
        {
            var response = webEx.Response as FtpWebResponse;
            if (response != null && (int)response.StatusCode < 400)
            {
                isTransient = true;
            }
        }
        // if it is a web exception but not a protocol error,
        // check the status code.
        else if (webEx != null)
        {
            //(check for transient error statuses here...)
            isTransient = true;
        }

        return isTransient;
    }

I'm trying to write some tests to check that things the appropriate errors are flagged as transient, but I'm having trouble creating or mocking a web exception that has an inner exception with an FtpWebResponse (so that the response in the following isn't always null)

var response = webEx.Response as FtpWebResponse;

Does anybody know how I can do this? Am I going about it the right way?


Solution

  • Use the appropriate constructor on WebException to set the response:

    public WebException(
     string message,
     Exception innerException,
     WebExceptionStatus status,
     WebResponse response)
    

    setting up an exception with an FtpWebResponse is the bit that I'm having trouble with... FtpWebResponse has an internal constructor that I can't access.

    The BCL is not really made for testing because that concept wasn't big at the time it was written. You'll have to call that internal constructor using reflection (use a decompiler to see what's available). Or, wrap all of the System.Net classes that you need with custom mockable classes. That looks like a lot of work, though.