Laravel5.1文件上传单元测试

作者:ZGJ


在软工第三阶段中,我彻底解决了上一阶段一直困扰我的文件上传单元测试问题,在这里做一个总结。

注:下文介绍中,方法一方法二实现简单但有一定的限制条件(也正因为如此我上一阶段中一直未能实现文件上传的单元测试),方法三即这一阶段摸索出来的方法,普遍性更高。

以下为正文:

方法一:伪造存储

具体见这篇博客

限制条件为laravel版本5.4以上;

方法二:表单交互

具体见这篇博客

限制条件为文件输入的前端元素必须包含于form表单中,否则测试报错;

下面详细介绍方法三:利用call函数

​ 之前的博客中我也提到,由于伪造存储和表单模拟输入的方式均失败,我试图阅读laravel底层代码,通过构造一些信息作为提交post请求的数据进行测试,但是各种方法均未能获得成功,

​ 总结一下失败的根本原因在于:在laravel5.1中,post函数提交的数据仅相当于$_POST全局变量对Request进行实例化,然而在laravel中,hasFile函数的判定使用的是Request实例中的files变量,该变量是利用$_FILE全局变量进行实例化的

​ 但是,laravel中对HTTP请求进行单元测试的原理在于模拟一次请求执行的过程,那么就必定涉及到请求的实例化,因此也一定有files(类似于$_FILE)变量的相关初始化和使用步骤,所以这一阶段中,通过对post,call等方法的源码阅读,我找到了解决该问题的突破口。

首先找到post函数,核心部分为这一句:

$this->call('POST', $uri, $data, [], [], $server);

实则还是调用call函数,因此看到call函数:

public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
{
$kernel = $this->app->make('Illuminate\Contracts\Http\Kernel'); $this->currentUri = $this->prepareUrlForRequest($uri); $request = Request::create(
$this->currentUri, $method, $parameters,
$cookies, $files, array_replace($this->serverVariables, $server), $content
); $response = $kernel->handle($request); $kernel->terminate($request, $response); return $this->response = $response;
}

看到call函数,令我马上眼前一亮的是其中一个参数:$files

再往下看其执行过程,这几乎就是laravel中index.php中的内容,再看其中请求的实例化:

$request = Request::create(

$this->currentUri, $method, $parameters,

$cookies, $files, array_replace($this->serverVariables, $server), $content

);

可以看到在call函数中是可以通过传入$files参数模拟文件上传的(当然还包括其他请求实例化所用的全局变量),如此强大的一个测试函数laravel官方文档中竟然只提到了parameters参数的传入而雪藏了其他的可用参数……

经过进一步的尝试,我彻底的解决了该问题,先付上该部分代码:

$pdf_info = [
'name' => public_path().'/prepare_pdf/phylab_test.pdf' ,
'error' => 0 ,
'type' => 'pdf' ,
'size' => 100000 ,
'tmp_name' => public_path().'/prepare_pdf/phylab_test.pdf' ,
] ;
$pdf = new UploadedFile($pdf_info['tmp_name'], $pdf_info['name'], $pdf_info['type'], $pdf_info['size'], $pdf_info['error'] , true);
self::assertTrue($pdf instanceof UploadedFile) ;
$file_arr = [
'prepare-pdf' => $pdf ,
] ;
$response = $this->call('POST' , '/console/uploadPre' , ['labID'=>'2134'] , [] , $file_arr);
$data = $response->getData() ;
self::assertEquals('上传成功' , $data->message) ;

接下来强调一下务必注意的三个坑点:

坑点1:UploadedFile的实例化必须带有test=true参数

注意这句代码:

$pdf = new UploadedFile($pdf_info['tmp_name'], $pdf_info['name'], $pdf_info['type'], $pdf_info['size'], $pdf_info['error'] , true);

对于UploadedFile的实例化包含6各参数,前五个很容易理解,也就是文件的五项信息,于$_FILE全局变量的结构相对应。最后一项参数不能省略,而且必须传入true,非常重要!

解释一下原因:

文件上传自然涉及到上传后文件的存储,而文件上传的存储涉及到move方法(具体控制器代码可见这篇博客

move方法中首先会利用isValid函数进行判断,如果判断失败,则会直接抛出异常。

再看isValid函数代码:

public function isValid()
{
$isOk = UPLOAD_ERR_OK === $this->error; return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
}

注意返回时用到了is_upload_file函数,PHP文档中可以看到如下解释说明:

is_uploaded_file — 判断文件是否是通过 HTTP POST 上传的

如果 filename 所给出的文件是通过 HTTP POST 上传的则返回 TRUE。这可以用来确保恶意的用户无法欺骗脚本去访问本不能访问的文件,例如 /etc/passwd。

​ 这种检查显得格外重要,如果上传的文件有可能会造成对用户或本系统的其他用户显示其内容的话。

​ 为了能使 is_uploaded_file() 函数正常工作,必须指定类似于 [$_FILES'userfile']['tmp_name'] 的变量,而在从客户端上传的文件名 [$_FILES'userfile']['name'] 不能正常运作。

​ 在PHP中,文件上传后有一个暂时存储的路径,相关设置可以在php.ini文件中找到,当然我不会贸然对其进行改动。由于测试中传入的文件名均直接采用服务器上的一个测试文件,所以这个函数的判定将始终返回false。

​ 但是我们注意到,在isValid函数返回的最后,用到了其test参数,如果test参数为true,则会跳过is_upload_file函数的判定,需要注意的是,test参数默认为false,这也就是为什么对UploadedFil初始化时必须带有test=true参数。

坑点2:$files参数的结构

可见代码中作为$files的参数为:

$file_arr = [

'prepare-pdf' => $pdf ,

] ;

必须为一个键对应着一个文件实例(文件实例属于类UploadedFile),不可直接将一个文件实例作为$files参数传入

坑点3:对实体文件进行操作需谨慎

​ 该测试中,我直接使用了服务器上的一个pdf测试文件名作为文件实例的文件名,所以相关测试操作也是直接对该实体文件进行操作,测试结束后将该文件还原,以确保不对网站其他功能造成可能的破坏以及下次测试的有效性

​ 以上即是laravel5.1文件上传测试的方法和需要注意的坑点,希望能帮助和我之前一样遇到问题的程序猿朋友,当然,这篇博客的相关内容不能确保完全正确,毕竟这些大部分是自己阅读源码不断尝试探索出的方法,如有错误望指正。

【技术博客】Laravel5.1文件上传单元测试的更多相关文章

  1. 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理

    服务器文档下载zip格式   刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...

  2. [技术博客] Django中文件的保存与访问

    [技术博客] Django中文件的保存与访问 在TextMarking项目开发中,数据库需要保存用户上传的文本文档. 原型设计:用户点击上传文本->保存文本->文本发送到后端保存为文件. ...

  3. [SAP ABAP开发技术总结]文本文件、Excel文件上传下传

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  4. Ajax技术——带进度条的文件上传

    1.概述 在实际的Web应该开发或网站开发过程中,经常需要实现文件上传的功能.在文件上传过程中,经常需要用户进行长时间的等待,为了让用户及时了解上传进度,可以在上传文件的同时,显示文件的上传进度条.运 ...

  5. 基于mvc三层架构和ajax技术实现最简单的文件上传

    前台页面提交文件 <!DOCTYPE html> <html><head> <meta name="viewport" content=& ...

  6. 使用Typora写博客,图片即时上传,无需第三方图床-EasyBlogImageForTypora

    背景 习惯使用markdown的人应该都知道Typora这个神器,它非常简洁高效.虽然博客园的在线markdown编辑器也不错,但毕竟是网页版,每次写东西需要登录系统-进后台-找到文章-编辑-保存草稿 ...

  7. django中博客后台将图片上传作为用户头像

    添加上传目录 # 如果不添加上传目录,仍然可以上传成功,默认为project目录,如果models.py定义了upload_to="目录名称",则会上传到"project ...

  8. laravel5.5文件上传

    /**     * 上传文件     * @param Request $request     * @return array     */    public function upload(Re ...

  9. hexo博客更新主题后上传Git操作

    克隆主题: git clone https://github.com/SuperKieran/TKL.git _config.yml文件中主题改为新增主题 # Extensions ## Plugin ...

随机推荐

  1. 【03】Jenkins:SonarQube

    写在前面的话 SonarQube 这个服务有些人熟悉,有些人陌生.对于我们这样的运维人员,我们需要了解的是,SonarQube 是一个代码质量管理平台,懂得怎么安装配置,这其实就差不多足够了.我们在 ...

  2. 对Haskell这门语言的基本认识

    Haskell语言的核心特征: 1. 函数式,而且是纯函数式(purely functional) 首先,引用一下维基百科上对“典型的函数式编程语言”的划分: 一: 纯函数式 1. 强静态类型: Mi ...

  3. Windows Server2008R2,ServerWin2012 R2设置自动登录注册表配置

    serverWin2008 R2 2012自动登录一般是通过control userpasswords2 命令修改,其实注册表修改更简单.复制以下保存为xx.reg文件导入即可即可. Windows ...

  4. 【开发工具】- Xshell过期了怎么办?

    点击下边链接下载免费版 http://www.netsarang.com/download/free_license.html

  5. Node.js返回JSON

    在使用JQuery的Ajax从服务器请求数据或者向服务器发送数据时常常会遇到跨域无法请求的错误,常用的解决办法就是在Ajax中使用JSONP.基于安全性考虑,浏览器会存在同源策略,然而<scri ...

  6. VMware + CentOS 7搭建环境(一)

    1. 下载VMware Workstation约300MB(vmware12虚拟机软件)12.5.2 官方简体中文版:http://www.xiazaiba.com/html/27692.htmlVM ...

  7. 字符串的sizeof长度及strlen长度

    在C/C++中,字符串是以零('\0')结尾的.比如,对于下面的字符串: "hello"  在最后一个字符'd'后面,还有一个我们肉眼看不见的'\0'字符,作为该字符串的结束符.所 ...

  8. 面试官:优化代码中大量的if/else,你有什么方案?

    一个快速迭代的项目,时间久了之后,代码中可能会充斥着大量的if/else,嵌套6.7层,一个函数几百行,简!直!看!死!人! 这个无限循环嵌套,只是总循环的一部分...我已经绕晕在黄桷湾立交 仔细数了 ...

  9. MySQL——基本概念

    1.数据库:是一个长期存储在计算机内的.有组织的.有共享的.统一管理的数据集合.它是一个按数据结构来存储的和管理数据的计算机软件系统,即数据库包含两层含义:保管数据的“仓库”,以及数据管理的方法和技术 ...

  10. qtcreator VLD内存检测

    简介 Visual Leak Detector是一款用于Visual C++的免费的内存泄露检测工具.相比较其它的内存泄露检测工具,它在检测到内存泄漏的同时,还具有如下特点: 可以得到内存泄漏点的调用 ...