组合使用Laravel和vfsStream测试文件上传

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33805743/article/details/94231883

核心要点

  • 在应用开发中,测试是很重要的,在诸多的开发工具中,测试驱动开发是很伟大的一项;
  • 测试文件上传并不像人们想象的那么简单;
  • 目前,有很多很棒,但不为大家所熟知的测试工具;
  • Larval能够让请求的校验更容易;
  • 测试并不需要实际的文件系统,因为如果这样做的话,会在项目中引入添加/移除文件的代码,从而导致噪音的出现。

文件上传的测试可能会非常棘手,但是通过使用恰当的工具,再掌握一些技巧,这个过程可以会更加高效,也能更容易。

如果你之前没有了解过vfsStream的话,那么简单来讲,它允许与保存在内存中的文件进行交互,而不是与机器中实际存在文件进行交互。这样做的好处在于我们并不需要删除用于测试的文件,如果测试失败,teardown或者其他用于移除测试文件的代码没有执行,那么这就更会成为一个问题了。除此之外,因为它处理的是内存而不是物理的硬件驱动器,所以它会更快。简而言之,它能够更加整洁,速度也会更快。

本文将会创建一个端点(路由)来上传包含用户信息的CSV文件,并测试CSV文件中的用户能够展现到一个JSON格式的响应中,同时还会添加校验功能,确保所处理的文件是CSV类型。这是一个超出现实的样例,不过它可以作为一个很好的起点,帮助你在项目中实现类似的功能。

在本文中,我们假设你已经安装好了Laravel(我们所使用的是5.3版本)。你不用实际去点击这个路由,但是我们建议你使用Laravel ValetHomestead。在这个指南中,不会包含如何让它们就绪并运行起来的内容,通过这个仓库的链接中,你可以获取本文所对应的代码。

起步

打开控制台并导航至项目的根文件夹(在这里你可以看到composer.json文件和.env文件等内容),如果你运行:

composer require mikey179/vfsStream --dev

那么就会将vfsStream拉取到composer file文件的development部分中,同时还会拉取所需的文件,从而能够在测试中使用这个包。完成这些之后,我们就为测试做好了准备。

采用的TDD的方式编写代码

为了让这个过程尽可能简单,只需在tests文件夹(同样在项目的根目录下)中创建一个名为UploadCsvTest.php的文件。现在,我们需要打开这个文件并将如下的内容添加进去。

\u0026lt;?phpclass UploadCsvTest extends TestCase{    public function testSuccessfulUploadingCsv()    {    }}

首先,我们让文件上传运行起来,然后可以再回过头来再添加校验功能。采取这种方式的原因在于,我们能够为可运行的代码迭代式地添加功能。例如,如果你能上传文件的话,那么它就是可运行的代码,为其添加校验是对功能的增强,可以稍后再进行。

现在,添加如下的代码到“testSuccessfullUploadingCsv”函数中:

$this-\u0026gt;json('POST', 'upload-new-users-csv', ['usersCsvFile' =\u0026gt; 'someFile']);$this-\u0026gt;assertResponseOk();

如果通过控制台运行(稍后,我们将会通过PHPUnit来引用这条命令)下面的命令的话:

phpunit --filter=UploadCsvTest

不出意料,这里会发生失败,看起来会像如下所示:

Expected status code 200, got 404.Failed asserting that false is true.

404响应意味着无法找到路由,现在我们来定义它。导航至routes/web.php文件(从上一个Laravel版本开始,路由文件已经从Http目录转移到了这里),并且在文件的底部添加下面的代码:

Route::post('/upload-new-users-csv', function (\\Illuminate\\Http\\Request $request) {});

现在,如果我们运行PHPUnit的话,测试就能通过了。所以,接下来我们就应该在路由文件的闭包中访问预期的文件了。因此,回到这个文件中,并在闭包中添加如下这行代码:

$request-\u0026gt;input('usersCsvFile')-\u0026gt;openFile();

此时,路由看起来如下所示:

Route::post('/upload-new-users-csv', function (\\Illuminate\\Http\\Request $request) {    $request-\u0026gt;input('usersCsvFile')-\u0026gt;openFile();});

现在,如果你运行PHPUnit的话,会得到另外的错误,这是一个500错误。在这里,要查清发生了什么需要一点技巧。我们可以在测试中通过如下的方式来查看响应的内容:

dd($this-\u0026gt;response-\u0026gt;getContent());

但是很重要的一点在于,它要在断言响应OK之前执行,否则的话,在我们得到dd输出之前,测试就已经失败了。另外一种方式就是查看位于“storage/logs/laravel.log”中的日志。不管采用哪种检查方式,我们都会看到类似于这样的信息:“使用字符串的形式调用了成员函数openFile()”,这其实就是我们要达到的效果,因为在测试中,当我们发起post请求时,发送的是一个字符串。接下来,该看一下vfs了!

熟悉vfsStream

它并不全面,但这是一个好的开端,这是一个非常深入和强大的包。为了让测试看起来更棒一些,我们先将创建文件的内容放到一个函数中。所以,回到测试文件(UploadCsvTest.php)中,将其修改成如下所示:

\u0026lt;?php use Illuminate\\Http\\UploadedFile;use org\\bovigo\\vfs\\vfsStream;class UploadCsvTest extends TestCase{    public function testSuccessfulUploadingCsv()    {        $this-\u0026gt;json('POST', 'upload-new-users-csv', ['usersCsvFile' =\u0026gt; $this-\u0026gt;createUploadFile()]);        $this-\u0026gt;assertResponseOk();    }    protected function createUploadFile()    {        $vfs = vfsStream::setup(sys_get_temp_dir(), null, ['testFile' =\u0026gt; 'someString']);        return new UploadedFile(            $vfs-\u0026gt;url().'/testFile',            null,            'text/csv',            null,            null,            true        );    }}

这里引入了很多内容,首先是vfsStream::setup,它会在内存中创建一个文件。样例中使用了“sys_get_temp_dir()”,这只是让它看起来“更真实一些”,这里可以使用任意的值,比如可以将其修改为字符串“root”,运行效果是完全一样的。这背后的理念是允许你创建任意的目录结构以适应实际的需求。

这其实并不太重要,因为PHP会自动将上传的文件放到系统的临时目录中,所以只需使用“sys_get_temp_dir()”就可以,在这个场景中,它其实并没有什么关系。下一个参数用来设置文件权限,在本例中,我们没有特殊的要求,所以只需将其设置为null,这样允许你对文件进行任意操作。最后,是目录(本例中也就是“/tmp”)中的内容,它只是一个数组,由文件名(key)和值(现在是普通的字符串,不过后续需要改为CSV内容)所组成。

最后,我们可以看到的是UploadedFile,它是对Symfony的UploadedFile的扩展,我们按顺序快速看一下它的参数(来源于Symfony的注释内容):

第一个参数:文件的完整临时目录;

第二个参数:原始的文件名;

第三个参数:PHP所提供的文件类型,如果是null的话,默认为application/octet-stream;

第四个参数:文件大小;

第五个参数:上传出现错误的常量(PHP的UPLOAD_ERR_XXX常量之一),如果是null的话,默认为UPLOAD_ERR_OK;

第六个参数:是否启用测试模式。

如果你运行PHPUnit的话,那么测试就能够通过了!现在,我们让文件看起来更真实一些,在createUploadFile函数中,将它的内容改为如下所示:

protected function createUploadFile(){    $vfs = vfsStream::setup(sys_get_temp_dir(), null, ['testFile.csv' =\u0026gt; '']);    $uploadFile = new UploadedFile(        $vfs-\u0026gt;url().'/testFile.csv',        null,        'text/csv',        null,        null,        true    );    collect([        ['username', 'first name', 'last name'],        ['jondoe', 'Jon', 'Doe'],        ['janedoe', 'Jane', 'Doe']    ])-\u0026gt;each(function ($fields) use ($uploadFile) {        $uploadFile-\u0026gt;openFile('a+')-\u0026gt;fputcsv($fields);    });    return $uploadFile;}

这里利用了Laravel的集合辅助工具,借助一些PHP的功能来填充文件。openFile()会得到底层的SPLFileObject,然后直接使用它,将数组中的元素添加到文件中,就像CSV文件中的某行数据一样。运行PHPUnit,我们依然会得到绿色的测试通过提示。现在,我们要确保能够得到预期的结果,响应中的JSON要列出文件中的内容。要实现这项功能,将testSuccessfulUpload函数改成如下所示:

public function testSuccessfulUploadingCsv(){    $this-\u0026gt;json('POST', 'upload-new-users-csv', ['usersCsvFile' =\u0026gt; $this-\u0026gt;createUploadFile()])        -\u0026gt;seeJson([            \"username,\\\"first name\\\
展开阅读全文
博主设置当前文章不允许评论。

没有更多推荐了,返回首页