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. BAT: Windows批处理更改当前工作路径

    最近项目上需要获取文件夹下所有文件信息,因为文件夹是在server上,所以想用批处理bat来获取该路径下所有文件信息,然后通过任务计划管理去每天自动运行bat去更新文件信息内容. 获取文件夹下所有文件 ...

  2. English--比较结构

    English|比较结构 接下来让我们一起来透析,英语中的比较结构.各位同学,带上小板凳,要认真哦~ 前言 目前所有的文章思想格式都是:知识+情感. 知识:对于所有的知识点的描述.力求不含任何的自我感 ...

  3. 我为啥不想用Python

    Python这门语言从一开始就是一个玩具语言,它不是给正经程序员用的东西. 运行效率低 Python运行效率很低,这就导致Python中很多库底层实际上是C++.很多时候,自己千方百计优化的结果就不如 ...

  4. wait waitpid

    定义 pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); 暂时停止进程的执行,直到有信号来到或子进 ...

  5. Keepalived与MySQL互为主从自动切换配置

    为解决Mysql数据库单点问题,实现两台MySQL数据库互为主备,双向replication.当一Master出现问题,则将Slave切换为Master继续工作. 环境说明 系统版本:CentOS L ...

  6. mysql后台线程详解

    1.mysql后台线程 mysql后台线程主要用于维持服务器的正常运行和完成用户提交的任务,主要包括:master thread,read thread,write thread,redo log t ...

  7. tomcat server.xml 中 host 元素

    测试偶然发现: <Host name="127.0.0.1" appBase="webapps" unpackWARs="true" ...

  8. (原)ubuntu中C++调用libotrch

    转载请注明出处: https://www.cnblogs.com/darkknightzh/p/11479240.html 参考网址: https://pytorch.org/tutorials/ad ...

  9. Odoo报表的report标签和报表格式定义

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826329.html 一:Report标签     report标签可用于定义一条报表记录.属性有: 1) ...

  10. 没有该栏目数据, 可能缓存文件(/data/cache/inc_catalog_base.inc)没有更新, 请检查是否有写入权限

    今天朋友在创建新栏目时出现了一个无法生成的错误,提示没有该栏目数据, 可能缓存文件(/data/cache/inc_catalog_base.inc)没有更新, 请检查是否有写入权限,其实这个相对比较 ...