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

核心要点

\
  • 在应用开发中,测试是很重要的,在诸多的开发工具中,测试驱动开发是很伟大的一项; \
  • 测试文件上传并不像人们想象的那么简单; \
  • 目前,有很多很棒,但不为大家所熟知的测试工具; \
  • 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;?php\\class 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\\\
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值