You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Laravel流式输出内容编写Feature测试?

Laravel流式CSV导出的Feature测试方案

一、解决测试时流式内容输出到终端的问题

由于SimpleExcelWriter::streamDownload会直接向输出缓冲区写入内容,测试时会导致CSV内容与测试日志混杂在终端。可以通过捕获输出缓冲区内容的方式避免该问题,同时获取CSV内容用于后续验证:

// 开启输出缓冲
ob_start();
$response = $this->actingAs($admin)->get(route('export.user', ['role' => RoleEnum::User->value]));
// 获取并清空缓冲区内容
$csvContent = ob_get_clean();

执行上述代码后,CSV内容会被保存到$csvContent变量中,不会输出到终端。

二、核心测试场景

针对该导出接口,需要覆盖以下测试点:

  • 权限验证:仅管理员能访问接口
  • 响应头验证:返回正确的Content-Type和下载文件名
  • CSV内容验证:表头正确、数据行数匹配、数据内容符合预期
  • 参数验证:传入无效角色时返回正确错误响应

三、完整测试代码实现

<?php

namespace Tests\Feature\Admin;

use App\Models\User;
use App\Enums\RoleEnum;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class UserExportTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function admin_can_export_users_by_role_and_get_valid_csv()
    {
        // 创建管理员用户
        $admin = User::factory()->admin()->create();
        
        // 创建4个普通角色用户
        $users = User::factory(4)->user()->create();
        
        // 捕获流式输出的CSV内容,避免终端混杂
        ob_start();
        $response = $this->actingAs($admin)->get(route('export.user', ['role' => RoleEnum::User->value]));
        $csvContent = ob_get_clean();

        // 1. 验证响应状态和头部
        $response->assertStatus(200);
        $response->assertHeader('Content-Type', 'text/csv');
        $response->assertHeader('Content-Disposition', 'attachment; filename="user.csv"');

        // 2. 解析CSV内容并验证
        $lines = explode("\n", trim($csvContent));
        
        // 验证总行数:1行表头 + 4行数据
        $this->assertEquals(5, count($lines));
        
        // 验证表头内容
        $expectedHeader = [
            'Company Name',
            'Account Code',
            'First Name',
            'Last Name',
            'Email',
            'Registration',
            'Email Verified',
            'Last Login',
            'Status',
            'Role'
        ];
        $this->assertEquals($expectedHeader, str_getcsv($lines[0]));

        // 验证第一条数据行(按first_name/last_name排序后的第一个用户)
        $firstUser = $users->sortBy('first_name')->sortBy('last_name')->first();
        $firstDataRow = str_getcsv($lines[1]);
        $this->assertEquals([
            $firstUser->company_name,
            $firstUser->account_code,
            $firstUser->first_name,
            $firstUser->last_name,
            $firstUser->email,
            $firstUser->local_created_at_date,
            $firstUser->local_email_verified_at_date,
            $firstUser->local_last_login_at_date,
            $firstUser->status->value,
            $firstUser->role->value,
        ], $firstDataRow);
    }

    /** @test */
    public function non_admin_cannot_access_user_export()
    {
        $regularUser = User::factory()->user()->create();

        $response = $this->actingAs($regularUser)->get(route('export.user', ['role' => RoleEnum::User->value]));
        
        // 根据权限控制逻辑返回403 Forbidden
        $response->assertStatus(403);
    }

    /** @test */
    public function invalid_role_returns_error()
    {
        $admin = User::factory()->admin()->create();

        $response = $this->actingAs($admin)->get(route('export.user', ['role' => 'invalid-role']));
        
        // 路由参数使用RoleEnum约束,自动返回404
        $response->assertStatus(404);
    }
}

四、额外说明

  • 若local_created_at_date等为模型访问器,需确保测试环境下访问器能返回预期值,必要时可在工厂中设置对应原始字段值。
  • str_getcsv会自动处理CSV中的特殊字符(如逗号、引号),无需额外处理。
  • 若需测试分块逻辑,可创建超过200个用户验证数据完整性,不过通常单元测试覆盖分块逻辑即可,Feature测试无需大量数据。

内容的提问来源于stack exchange,提问作者dfaltermier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 01:50:21