在Laravel测试流响应

Laravel提供了一个方便的API来生成HTTP响应,迫使用户的浏览器下载给定URL的文件。在编写应用程序中包含文件下载的功能时,Laravel提供了一种愉快的测试体验,使编写和测试可下载文件变得轻而易举。 p>让我们看一个在Laravel应用程序中创建和测试可下载URL的实际示例。 h2>简介,我们将建立一个快速的users导出到CSV功能,允许用户下载数据库中所有用户的CSV导出。为了强制用户下载,Laravel有一个download方法,它接受一个文件路径(如响应文档中所述):如果您提供客户、用户的导出,那么 1// Download response for a filesystem file2return response()->download($pathToFile, $name, $headers); ,或另一个数据库记录response()->streamDownload()真是太棒了: 1return response()->streamDownload(function () {2echo GitHub::api(\"repo\")3->contents()4->readme(\"laravel\", \"laravel\")[\"contents\"];5}, \"laravel-readme.md\"); 本教程是如何编写和测试控制器响应流文件响应的快速演示。我想指出的是,在一个实际的应用程序中,您应该提供一些安全机制来根据应用程序的业务规则导出用户。 h2>首先,我们需要创建一个新的Laravel应用程序并构建身份验证: pre>***5测试环境。本教程将使用测试来驱动该特性,您可以自由配置任何类型的数据库,以便在浏览器中试用该应用程序。 打开项目根目录中的phpunit.xml文件并添加以下环境变量: 12345 我们搭建了一个快速Laravel应用程序并生成了身份验证保护用户导出路径所需的文件。我们已经准备好开始测试我们的导出和编写控制器了。编写测试 我们将用PHPUnit特性测试来驱动这个特性,并在运行时构建控制器逻辑。 首先,我们将创建一个新的特性测试: 1php artisan make:test UsersExportTest 在这个文件中,我们将构建第一个测试,这是当尝试访问/users/export时,来宾被重定向到登录URL。 1get(\"/users/export\")15->assertStatus(302)16->assertLocation(\"/login\");17}18} 我们请求导出终结点,并期望对登录页的重定向响应。 我们甚至还没有定义路由,因此当我们运行测试时,此测试将失败: 1$ phpunit> 2PHPUnit 7.5.9 by Sebastian Bergmann and contributors. 3 4F1 / 1 (100%) 5 6Time: 113 ms, Memory: 14.00 MB 7 8There was 1 failure: 9101) TestsFeatureUsersExportTest::guests_cannot_download_users_export11Expected status code 302 but received 404.12Failed asserting that false is true. 我们还没有定义路由,现在让我们在routes/web.php文件中这样做: 1Route::get(\"/users/export\", \"UsersExportController\"); 重新运行测试会带来下一个需要修复的故障: 1phpunit> 2PHPUnit 7.5.9 by Sebastian Bergmann and contributors. 3 4E1 / 1 (100%) 5 6Time: 96 ms, Memory: 12.00 MB 7 8There was 1 error: 9101) TestsFeatureUsersExportTest::guests_cannot_download_users_export11UnexpectedValueException: Invalid route action: [AppHttpControllersUsersExportController]. 接下来,生成控制器作为可调用的操作。我发现对于文件导出,我倾向于使用一个可调用的控制器,因为我希望在我典型的RESTful路由之外明确显示此控制器的意图。 在运行此命令之前,您需要注释掉我们添加到routes/web.php文件的路由: 1// Route::get(\"/users/export\", \"UsersExportController\"); 现在您可以使用artisan命令创建一个新的可调用控制器: 1php artisan make:controller -i UsersExportController 取消注释我们添加的路由并重新运行测试以获得下一个测试失败: 1phpunit> 2PHPUnit 7.5.9 by Sebastian Bergmann and contributors. 3 4F1 / 1 (100%) 5 6Time: 122 ms, Memory: 14.00 MB 7 8There was 1 failure: 9101) TestsFeatureUsersExportTest::guests_cannot_download_users_export11Expected status code 302 but received 200. 我们需要添加的最后一个代码更改是auth中间件来保护路线不受客人影响。我们不假设任何用户对本教程有任何权限可以下载用户的导出。 1Route::get(\"/users/export\", \"UsersExportController\")2->middleware(\"auth\"); 最后,我们的测试应该通过: 1phpunit>2PHPUnit 7.5.9 by Sebastian Bergmann and contributors.34.1 / 1 (100%)56Time: 118 ms, Memory: 14.00 MB78OK (1 test, 2 assertions)启动导出功能 我们已经准备好用户导出测试和控制器,并且我们已经准备好开始测试实际的流式下载功能。我们的第一步是在UsersExportTest测试类中创建下一个测试用例,该测试类请求导出端点作为经过身份验证的用户。 1// Make sure to import the RefreshDatabase trait 2use RefreshDatabase; 3 4/** @test */ 5public function authenticated_users_can_export_all_users() 6{> 8>11dd($content);12} 请注意TestResponse实例上的streamedContent()方法。这是一个很好的帮助方法,可以从StreamResponse实例获取字符串形式的内容(请查看Symfony HttpFoundation组件)以获取有关StreamResponse类的更多信息。 我们的测试构建了五个用户,并使用集合中的第一个用户向导出端点发出经过身份验证的请求。由于我们没有编写任何控制器代码,我们的测试将无法断言我们没有StreamResponse实例: 1$ phpunit> 2PHPUnit 7.5.9 by Sebastian Bergmann and contributors. 3 4F1 / 1 (100%) 5 6Time: 183 ms, Memory: 20.00 MB 7 8There was 1 failure: 9101) TestsFeatureUsersExportTest::authenticated_users_can_export_all_users11The response is not a streamed response. 让我们用所需的少量代码通过这个测试。将可调用的UsersExportController类更改为以下内容: 1/** 2 * Handle the incoming request. 3 * 4 * @paramIlluminateHttpRequest$request 5 * @return IlluminateHttpResponse 6 */ 7public function __invoke(Request $request) 8{ 9return response()->streamDownload(function () {10echo \"hello world\";11}, \"users.txt\");12} 如果我们重新运行PHPUnit测试,我们应该拥有StreamResponse实例的字符串表示形式: 1$ phpunit>2PHPUnit 7.5.9 by Sebastian Bergmann and contributors.34\"hello world\" 此外,如果您想试验content disposition头并断言响应确实正在强制下载,您可以添加下面几行: 1/** @test */ 2public function authenticated_users_can_export_all_users() 3{> 5> 7$response->assertHeader(\"Content-Disposition\", \"attachment;>> 9dd($content);10} Laravel对streamDownload功能进行了测试,但是检查头部以确保我们正在强制下载导出端点以确保我们正在从控制器发送适当的响应头部不会有什么影响。 测试CSV导出为了测试user.mailer实例,我们的控制器返回,现在是时候继续生成和测试一个CSV导出了。 我们将依赖PHP League CSV包来生成和测试我们的端点: 1// AppServiceProvider.class 2 3publicfunctionregister() 4{ 5$this->app->bind(\"user.mailer\", function ($app, $parameters) {>>>>11141719$transport->setUsername($smtp_username);20$transport->setPassword($smtp_password);21$transport->setEncryption($smtp_encryption);222426$mailer->alwaysFrom($from_email, $from_name);27$mailer->alwaysReplyTo($from_email, $from_name);2829return $mailer;30 });31} 下一步,让我们继续编写我们的测试,首先断言CSV行数与用户数匹配在数据库中: Mailer 我们首先创建一个新的CSV读卡器实例,并将头偏移量设置为第一行(我们尚未在控制器中写入CSV文件),这将很快与我们的CSV列匹配。设置头偏移量意味着>>>>>> 7>>10];111314// Use $mailer here...CSV reader实例将忽略作为行计数一部分的头行。 要通过此过程,我们需要创建一个CSV writer实例并添加我们的用户: Mailables 这里有很多要解包的内容,但最重要的是,我们创建了一个CSV writer实例,并将数据库中的用户添加到writer中。最后,我们在Mailables闭包中输出CSV文件的内容。 还要注意,我们将文件名更改为Mail,需要调整测试以匹配。让我们也检查一下流式内容,看看我们的原始CSV文件的作用: Mailer 您应该会看到我们在测试中添加的五个工厂用户的如下内容: 最后,如果您删除对ShouldQueue的调用,测试现在应该通过: Mailable 下面是我们的测试用例现在的样子: 1classSomeMailableextendsMailableimplementsShouldQueue2{3// Mailable code here4}检查记录 我们现在可以通过在测试: 1classSomeMailableextendsMailable2{3// Mailable code here4} 我们的上一个测试用例有很多新行,但它们并不超级复杂。我们首先从工厂的Mailer变量中分别查询数据库中的所有记录。我们希望直接从数据库中获取所有用户的新集合。 接下来,我们验证CSV文件中的行数是否与数据库集合匹配。使用CSV行,我们在UserMailerJob集合中搜索用户,以确保我们为每个用户提供帐户。我们断言列的格式,并最终将用户从循环底部的Mailable集合中删除。 最终断言通过在CSV文件中表示为一行来保证所有用户都从临时集合中删除。结论 当我们开始编写和测试此功能的详细信息时,本教程的一大收获是使用 1classUserMailerJobimplementsShouldQueue 2{ 3useDispatchable, InteractsWithQueue, Queueable, SerializesModels; 4 5public $configuration; 6public $to; 7public $mailable; 8 9/**10* Create a new job instance.11*12* @paramarray $configuration13* @paramstring $to14* @paramMailable $mailable15*/16publicfunction__construct(array $configuration, string $to, Mailable $mailable)17{21}2223/**24* Execute the job.25*26* @returnvoid27*/28publicfunctionhandle()29{31$mailer->to($this->to)->send($this->mailable);32}33}方法获取流文件以验证内容。本教程最简洁的部分之一是实现了从流生成纯文本文件而不必先将文件保存到磁盘!在我看来,将模型数据表示为流式下载而不导出文件的可能性是一个极好的特性!

admin_action_{$_REQUEST[‘action’]}

do_action( "admin_action_{$_REQUEST[‘action’]}" )动作钩子::在发送“Action”请求变量时激发。Action Hook: Fires when an ‘action’ request variable is sent.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$_REQUEST['action']引用从GET或POST请求派生的操作。源码(Source)更新版本源码位置使用被使用2.6.0 wp-admin/admin.php:...

日期:2020-09-02 17:44:16 浏览:1127

admin_footer-{$GLOBALS[‘hook_suffix’]}

do_action( "admin_footer-{$GLOBALS[‘hook_suffix’]}", string $hook_suffix )操作挂钩:在默认页脚脚本之后打印脚本或数据。Action Hook: Print scripts or data after the default footer scripts.目录锚点:#说明#参数#源码说明(Description)钩子名的动态部分,$GLOBALS['hook_suffix']引用当前页的全局钩子后缀。参数(Parameters)参数类...

日期:2020-09-02 17:44:20 浏览:1032

customize_save_{$this->id_data[‘base’]}

do_action( "customize_save_{$this->id_data[‘base’]}", WP_Customize_Setting $this )动作钩子::在调用WP_Customize_Setting::save()方法时激发。Action Hook: Fires when the WP_Customize_Setting::save() method is called.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_data...

日期:2020-08-15 15:47:24 浏览:775

customize_value_{$this->id_data[‘base’]}

apply_filters( "customize_value_{$this->id_data[‘base’]}", mixed $default )过滤器::过滤未作为主题模式或选项处理的自定义设置值。Filter Hook: Filter a Customize setting value not handled as a theme_mod or option.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_date['base'],指的是设置...

日期:2020-08-15 15:47:24 浏览:866

get_comment_author_url

过滤钩子:过滤评论作者的URL。Filter Hook: Filters the comment author’s URL.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/comment-template.php:32610...

日期:2020-08-10 23:06:14 浏览:903

network_admin_edit_{$_GET[‘action’]}

do_action( "network_admin_edit_{$_GET[‘action’]}" )操作挂钩:启动请求的处理程序操作。Action Hook: Fires the requested handler action.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$u GET['action']引用请求的操作的名称。源码(Source)更新版本源码位置使用被使用3.1.0 wp-admin/network/edit.php:3600...

日期:2020-08-02 09:56:09 浏览:848

network_sites_updated_message_{$_GET[‘updated’]}

apply_filters( "network_sites_updated_message_{$_GET[‘updated’]}", string $msg )筛选器挂钩:在网络管理中筛选特定的非默认站点更新消息。Filter Hook: Filters a specific, non-default site-updated message in the Network admin.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分$_GET['updated']引用了非默认的...

日期:2020-08-02 09:56:03 浏览:834

pre_wp_is_site_initialized

过滤器::过滤在访问数据库之前是否初始化站点的检查。Filter Hook: Filters the check for whether a site is initialized before the database is accessed.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/ms-site.php:93910...

日期:2020-07-29 10:15:38 浏览:809

WordPress 的SEO 教学:如何在网站中加入关键字(Meta Keywords)与Meta 描述(Meta Description)?

你想在WordPress 中添加关键字和meta 描述吗?关键字和meta 描述使你能够提高网站的SEO。在本文中,我们将向你展示如何在WordPress 中正确添加关键字和meta 描述。为什么要在WordPress 中添加关键字和Meta 描述?关键字和说明让搜寻引擎更了解您的帖子和页面的内容。关键词是人们寻找您发布的内容时,可能会搜索的重要词语或片语。而Meta Description则是对你的页面和文章的简要描述。如果你想要了解更多关于中继标签的资讯,可以参考Google的说明。Meta 关键字和描...

日期:2020-10-03 21:18:25 浏览:1620

谷歌的SEO是什么

SEO (Search Engine Optimization)中文是搜寻引擎最佳化,意思近于「关键字自然排序」、「网站排名优化」。简言之,SEO是以搜索引擎(如Google、Bing)为曝光媒体的行销手法。例如搜寻「wordpress教学」,会看到本站的「WordPress教学:12个课程…」排行Google第一:关键字:wordpress教学、wordpress课程…若搜寻「网站架设」,则会看到另一个网页排名第1:关键字:网站架设、架站…以上两个网页,每月从搜寻引擎导入自然流量,达2万4千:每月「有机搜...

日期:2020-10-30 17:23:57 浏览:1264