C# – unit-testable way to upload files to ASP.NET WebAPI

asp.net-web-apicfile uploadtdd

I'm working in a project that uses the new ASP.NET WebAPI. My current task is to accept an uploaded file. So far, I have used TDD to drive out the WebAPI code, but I've hit a wall with uploading. I'm currently following the advice found at http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2, but there seems to be no way at all to drive this out of a unit test. In order to get at the file and form data, I have to use MultipartFormDataStreamProvider, which is impossible to mock and/or override. Short of forsaking my TDD approach, what can I do?

Here's the code from the example:

public Task<HttpResponseMessage> PostFormData()
{
    // Check if the request contains multipart/form-data.
    if (!Request.Content.IsMimeMultipartContent())
    {
        throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
    }

    string root = HttpContext.Current.Server.MapPath("~/App_Data");
    var provider = new MultipartFormDataStreamProvider(root);

    // Read the form data and return an async task.
    var task = Request.Content.ReadAsMultipartAsync(provider).
        ContinueWith<HttpResponseMessage>(t =>
        {
            if (t.IsFaulted || t.IsCanceled)
            {
                Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
            }

            // This illustrates how to get the file names.
            foreach (MultipartFileData file in provider.FileData)
            {
                Trace.WriteLine(file.Headers.ContentDisposition.FileName);
                Trace.WriteLine("Server file path: " + file.LocalFileName);
            }
            return Request.CreateResponse(HttpStatusCode.OK);
        });

    return task;
}

The first problem is this line:

var provider = new MultipartFormDataStreamProvider(root);

For starters, to unit test this code, I need to be able to inject such a provider. It does WAY too much in that simple constructor call to be "newing it up" in line. There's got to be another way. (If not, WebAPI fails)

Best Answer

I abstracted out a provider wrapper so I could mock those moving parts, something like

    public interface IMultiPartFormDataStreamProviderWrapper : IDependency
    {
        string LocalFileName { get; }
        MultipartFormDataStreamProvider Provider { get; }
    }

    public class MultiPartFormDataStreamProviderWrapper : IMultiPartFormDataStreamProviderWrapper
    {
        public const string UploadPath = "~/Media/Default/Vocabulary/";
        private MultipartFormDataStreamProvider provider;

        public MultiPartFormDataStreamProviderWrapper(IHttpContextAccessor httpContextAccessor)
        {
            provider = new CustomMultipartFormDataStreamProvider(httpContextAccessor.Current().Server.MapPath(UploadPath));
        }

        public string LocalFileName
        {
            get { return provider.FileData[0].LocalFileName; }
        }


        public MultipartFormDataStreamProvider Provider
        {
            get { return provider; }
        }
    }

So I could then do something like

    if (Request.Content.IsMimeMultipartContent())
    {
        return Request.Content.ReadAsMultipartAsync(provider.Provider).ContinueWith(t => 
                    {
                        if (t.IsCanceled || t.IsFaulted)
                            return (object)new { success = false };

Not ideal, but gives some piece of mind. What do you think?