【问题标题】:Is there a unit-testable way to upload files to ASP.NET WebAPI?是否有一种可单元测试的方式将文件上传到 ASP.NET WebAPI?
【发布时间】:2012-11-13 00:41:16
【问题描述】:

我正在使用新的 ASP.NET WebAPI 的项目中工作。我目前的任务是接受上传的文件。到目前为止,我已经使用 TDD 来驱动 WebAPI 代码,但是我在上传时遇到了麻烦。我目前正在遵循http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2 的建议,但似乎根本无法将其排除在单元测试之外。为了获取文件和表单数据,我必须使用MultipartFormDataStreamProvider,这是无法模拟和/或覆盖的。如果没有放弃我的 TDD 方法,我该怎么办?

这是示例中的代码:

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;
}

第一个问题是这一行:

var provider = new MultipartFormDataStreamProvider(root);

对于初学者,要对这段代码进行单元测试,我需要能够注入这样的提供程序。在那个简单的构造函数调用中,它确实太多了,以至于“更新它”。一定有别的办法。 (如果不是,WebAPI 失败)

【问题讨论】:

  • 你能提供一些你正在使用的代码吗?顺便说一句,没有什么是不可能的..
  • 这个链接对你有帮助吗asp.net/web-api/overview/working-with-http/…
  • 您要测试上传过程的哪一部分?控制器?还是保存文件的代码?
  • @Matt Phillips,我想分别测试两者。我将我可以提取的内容提取到 UploadHandler 类中,但流提供程序是一个大问题。
  • 可以在这里找到更新的、直接的答案:stackoverflow.com/a/27961019/264628

标签: c# file-upload tdd asp.net-web-api


【解决方案1】:

我抽象出了一个提供者包装器,这样我就可以模拟那些移动的部分,比如

    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; }
        }
    }

所以我可以做类似的事情

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

不理想,但给人一些印象。你怎么看?

【讨论】:

  • 你能把这个拆开吗?它甚至没有像发布的那样编译。
【解决方案2】:

如果您还没有放弃 Web API,您可以尝试 System.Net.Http.TestableMultipartStreamProviders,这是对 Microsoft 流提供程序的直接重写。它们的好处是它们依赖SystemWrapper 进行文件操作,这意味着可以在单元测试中模拟文件操作。 wiki 提供了一些关于利用 DI 来减少测试控制器的痛苦的想法。

【讨论】:

    【解决方案3】:

    如果您使用self-hosting 功能,您可以编写一个单元测试:

    • 启动控制器(以及各种其他格式化程序/过滤器/等)
    • 使用 HttpClient(或者我个人,我会使用 RestSharp)将文件提交到该控制器(使用 RestSharp,您可以使用 AddFile 函数来执行此操作)
    • 根据需要验证输入流(例如,通过覆盖提供程序或仅检查传递给测试控制器的值或其他方式)

    【讨论】:

    • 我写了一篇关于在测试夹具中使用 WebAPI 自托管功能的博文awkwardcoder.blogspot.co.uk/2012/10/…
    • 自托管测试是真正的集成测试,而不是单元测试;如果您至少不能模拟文件系统,那么您就不是单元测试
    【解决方案4】:

    答案是“不”。 ASP.NET 是一个基于继承的框架。如果您尝试编写基于组合的应用程序,您将在某些时候发现摩擦和障碍。是时候切换到Nancy 之类的东西了。

    【讨论】:

    • 在上面添加了答案,想知道你的想法
    • 不知道为什么这被否决了,因为没有评论。提出这个问题时使用的 webapi 版本没有办法“单元测试”文件的上传。测试是勉强可以的,但是有很多额外的管道代码,并且只产生了一个“集成测试”,而不是一个单元测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-03
    • 2016-01-19
    相关资源
    最近更新 更多