Laravel PHPUnit如何测试含真实/假数据的Excel文件导入功能
Laravel Excel导入功能PHPUnit测试异常排查与解决
问题概述
在Laravel项目中使用MaatWebsite/Excel实现图书Excel导入功能,生产环境上传文件可正常导入数据库,但PHPUnit测试导入路由时出现异常:
- 测试代码尝试读取
test/Files/bookdata.xlsx真实文件,但请求失败 - 开启
withoutExceptionHandling()后,程序卡在异常处理环节 - 尝试生成假Excel文件时,无内容的文件触发业务报错,且不知道如何生成带有效内容的假文件
测试使用的Excel文件内容:
| category | Book Code | Book Title | Author | Publisher | Description | Stock |
|---|---|---|---|---|---|---|
| 1 | CO4D3E | Tittle 1 | Ex.Name1 | Publish1 | BookDesc1 | 3 |
| 3 | 3XA8PL | Tittle 2 | Ex.Name2 | Publish2 | BookDesc2 | 1 |
错误原因分析
1. 真实文件上传方式错误
测试代码中用UploadedFile::fake()->create()加载真实文件是错误的,该方法用于生成假文件,而非读取本地真实文件,正确做法是用UploadedFile实例直接加载真实文件路径。
2. 路由调用错误
测试中post('book.import')将字符串当作URL路径,而非路由名称,正确的路由调用应使用route('book.import')生成合法URL。
3. Excel模拟与真实导入冲突
Excel::fake()会模拟Excel导入行为,不会执行真实的文件解析逻辑。若要测试真实导入业务(如数据入库),需移除Excel::fake(),或配合模拟断言使用。
4. 控制器方法调用错误
控制器中$import->getRowCount缺少方法调用括号(),导致传递给通知的是方法对象而非行数值,触发潜在错误。
修正后的测试代码
方案1:使用真实文件测试完整业务逻辑
public function test_book_import(): void { // 预创建分类数据,匹配Excel中的category值 Categories::factory(6)->create(); // 加载真实Excel文件 $filePath = base_path('test/Files/bookdata.xlsx'); $file = new UploadedFile( $filePath, 'bookdata.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null, true // 标记为测试环境上传,绕过文件大小限制 ); // 发送请求到正确路由 $response = $this->post(route('book.import'), ['file' => $file]); // 断言重定向和成功提示 $response->assertRedirect(route('book.create')); $response->assertSessionHas('success', 'Excel file imported successfully!'); // 断言数据库导入数据正确性 $this->assertDatabaseCount('books', 2); $this->assertDatabaseHas('books', [ 'book_code' => 'CO4D3E', 'book_title' => 'Tittle 1', 'stock' => 3 ]); }
方案2:生成带内容的假Excel文件测试
若不想依赖真实文件,可通过PhpSpreadsheet生成带内容的假文件:
public function test_book_import_with_fake_file(): void { Categories::factory(6)->create(); // 生成假Excel内容 $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); // 写入表头 $sheet->setCellValue('A1', 'category'); $sheet->setCellValue('B1', 'Book Code'); $sheet->setCellValue('C1', 'Book Title'); $sheet->setCellValue('D1', 'Author'); $sheet->setCellValue('E1', 'Publisher'); $sheet->setCellValue('F1', 'Description'); $sheet->setCellValue('G1', 'Stock'); // 写入数据行 $sheet->setCellValue('A2', 1); $sheet->setCellValue('B2', 'CO4D3E'); $sheet->setCellValue('C2', 'Tittle 1'); $sheet->setCellValue('D2', 'Ex.Name1'); $sheet->setCellValue('E2', 'Publish1'); $sheet->setCellValue('F2', 'BookDesc1'); $sheet->setCellValue('G2', 3); // 保存到临时文件 $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); $tempFile = tempnam(sys_get_temp_dir(), 'excel'); $writer->save($tempFile); // 创建UploadedFile对象 $file = new UploadedFile( $tempFile, 'fake_bookdata.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null, true ); $response = $this->post(route('book.import'), ['file' => $file]); // 断言逻辑 $response->assertRedirect(route('book.create')); $response->assertSessionHas('success', 'Excel file imported successfully!'); $this->assertDatabaseCount('books', 1); }
控制器代码修正
修正getRowCount的调用方式:
// 原代码 Notification::send($users, new ImportBookNotification($import->getRowCount)); // 修正后 Notification::send($users, new ImportBookNotification($import->getRowCount()));
测试Excel导入的实用技巧
- 区分模拟与真实测试:
- 用
Excel::fake()可快速验证导入动作是否触发(如Excel::assertImported('filename.xlsx')),适合测试请求流程,无需关心文件内容 - 若要验证业务逻辑(数据入库、行计数等),需使用真实文件或生成带内容的假文件,不要启用
Excel::fake()
- 用
- 开启异常处理排查:测试时临时开启
$this->withoutExceptionHandling(),可直接查看具体错误信息(如文件不存在、数据验证失败等),避免卡在异常处理环节 - 验证数据完整性:除断言重定向和session消息,务必断言数据库数据是否符合预期,确保导入逻辑正确
- 复用测试数据:封装生成假Excel的逻辑为测试辅助方法,在多个测试用例中复用
内容的提问来源于stack exchange,提问作者Kholid Syaifullah
相关产品推荐
相关产品推荐

