Dashboard > MonoRail > Home > Testing file upload actions
Testing file upload actions
Added by Lee Henson, last edited by Lee Henson on Jul 11, 2007
Labels: 
(None)


If you want to test-drive a file upload action, you will run into the age-old problem of System.Web's sealed classes - specifically HttpPostedFile. One solution I am using successfully is to utilise MRs excellent IParameterBinder interface to allow inject a "seam" into the fabric of the request pipeline, which you can then open up during testing.

Less talk, more action. Let's create an interface to HttpPostedFile called IHttpPostedFileAdapter and then create a class implementing it called HttpPostedFileAdapter:

public class HttpPostedFileAdapter : IHttpPostedFileAdapter
{
    private HttpPostedFile httpPostedFile;

    public HttpPostedFileAdapter(HttpPostedFile httpPostedFile)
    {
        this.httpPostedFile = httpPostedFile;
    }

    public void SaveAs(string fileName)
    {
        httpPostedFile.SaveAs(fileName);
    }

    public int ContentLength
    {
        get { return httpPostedFile.ContentLength; }
    }

    public string ContentType
    {
        get { return httpPostedFile.ContentType; }
    }

    public string FileName
    {
        get { return httpPostedFile.FileName; }
    }

    public Stream InputStream
    {
        get { return httpPostedFile.InputStream; }
    }
}

 This wraps a standard HttpPostedFile with an interface which we now control.

 Next, lets utilise the IParameterBinder interface to create an attribute which we can use within our action signature:

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class HttpPostedFileAdapterBinderAttribute : Attribute, IParameterBinder
{
    private String filesKey;

    /// <summary>
    /// The files entry to use. If none
    /// is provided, the target parameter name is used
    /// </summary>
    public String FilesKey
    {
        get { return filesKey; }
        set { filesKey = value; }
    }

    public object Bind(SmartDispatcherController controller, ParameterInfo parameterInfo)
    {
        String key = ObtainKey(parameterInfo);
        HttpPostedFile httpPostedFile = controller.Context.Request.Files[key] as HttpPostedFile;

        return httpPostedFile == null ? null : new HttpPostedFileAdapter(httpPostedFile);
    }

    public int CalculateParamPoints(SmartDispatcherController controller, ParameterInfo parameterInfo)
    {
        String key = ObtainKey(parameterInfo);

        return controller.Context.Request.Files[key] != null ? 10 : 0;
    }

    private String ObtainKey(ParameterInfo parameterInfo)
    {
        return filesKey != null ? filesKey : parameterInfo.Name;
    }
}

Now, the upload action method signature can evolve from:

public void ProcessUpload(HttpPostedFile file) { ... }

to

public void ProcessUpload([HttpPostedFileAdapterBinder] IHttpPostedFileAdapter file) { ... }

Meaning we can now mock the upload easily, using say Rhino Mocks or a concrete mock class which inherits from IHttpPostedFileAdapter.

Site running on a free Atlassian Confluence Community License granted to Castle Project. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.4 Build:#809 Jun 12, 2007) - Bug/feature request - Contact Administrators