如何创建一个文件来填充HttpContext.Current.Request.Files?

A-S*_*ani 5 c# unit-testing mocking asp.net-web-api

在我的Web API中,POST操作方法在服务器上上传文件.

对于单元测试此方法,我需要创建一个HttpContext并将一个文件放在其请求中:

HttpContext.Current.Request.Files
Run Code Online (Sandbox Code Playgroud)

到目前为止,我正在使用此代码伪装HttpContext,它完美地运行:

  HttpRequest request = new HttpRequest("", "http://localhost/", "");
  HttpResponse response = new HttpResponse(new StringWriter());
  HttpContext.Current = new HttpContext(request, response);
Run Code Online (Sandbox Code Playgroud)

请注意,我不想使用Moq或任何其他Mocking库.

我怎么能做到这一点?(多部分内容可能吗?)

谢谢

Stu*_*tLC 7

由于大多数基础设施隐藏在密封或内部类中,我最终能够HttpContext通过大量使用反射来为WebApi单元测试添加假文件Request.Files.

添加下面的代码后,可以相对轻松地将文件添加到HttpContext.Current:

var request = new HttpRequest(null, "http://tempuri.org", null);
AddFileToRequest(request, "File", "img/jpg", new byte[] {1,2,3,4,5});

HttpContext.Current = new HttpContext(
    request,
    new HttpResponse(new StringWriter());
Run Code Online (Sandbox Code Playgroud)

随着繁重的工作完成:

static void AddFileToRequest(
    HttpRequest request, string fileName, string contentType, byte[] bytes)
{
    var fileSize = bytes.Length;

    // Because these are internal classes, we can't even reference their types here
    var uploadedContent = ReflectionHelpers.Construct(typeof (HttpPostedFile).Assembly,
        "System.Web.HttpRawUploadedContent", fileSize, fileSize);
    uploadedContent.InvokeMethod("AddBytes", bytes, 0, fileSize);
    uploadedContent.InvokeMethod("DoneAddingBytes");

    var inputStream = Construct(typeof (HttpPostedFile).Assembly,
        "System.Web.HttpInputStream", uploadedContent, 0, fileSize);

    var postedFile = Construct<HttpPostedFile>(fileName, 
             contentType, inputStream);
    // Accessing request.Files creates an empty collection
    request.Files.InvokeMethod("AddFile", fileName, postedFile);
}

public static object Construct(Assembly assembly, string typeFqn, params object[] args)
{
    var theType = assembly.GetType(typeFqn);
    return theType
      .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, 
             args.Select(a => a.GetType()).ToArray(), null)
      .Invoke(args);
}

public static T Construct<T>(params object[] args) where T : class
{
    return Activator.CreateInstance(
        typeof(T), 
        BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
        null, args, null) as T;
}

public static object InvokeMethod(this object o, string methodName, 
     params object[] args)
{
    var mi = o.GetType().GetMethod(methodName, 
             BindingFlags.NonPublic | BindingFlags.Instance);
    if (mi == null) throw new ArgumentOutOfRangeException("methodName",
        string.Format("Method {0} not found", methodName));
    return mi.Invoke(o, args);
}
Run Code Online (Sandbox Code Playgroud)