如何使用 laravel 和 phpunit 测试文件上传?
Posted
技术标签:
【中文标题】如何使用 laravel 和 phpunit 测试文件上传?【英文标题】:How to test file upload with laravel and phpunit? 【发布时间】:2016-01-21 18:23:59 【问题描述】:我正在尝试在我的 laravel 控制器上运行此功能测试。我想测试图像处理,但这样做我想伪造图像上传。我该怎么做呢?我在网上找到了一些例子,但似乎没有一个对我有用。这是我所拥有的:
public function testResizeMethod()
$this->prepareCleanDB();
$this->_createAccessableCompany();
$local_file = __DIR__ . '/test-files/large-avatar.jpg';
$uploadedFile = new Symfony\Component\HttpFoundation\File\UploadedFile(
$local_file,
'large-avatar.jpg',
'image/jpeg',
null,
null,
true
);
$values = array(
'company_id' => $this->company->id
);
$response = $this->action(
'POST',
'FileStorageController@store',
$values,
['file' => $uploadedFile]
);
$readable_response = $this->getReadableResponseObject($response);
但是控制器没有通过这个检查:
elseif (!Input::hasFile('file'))
return Response::error('No file uploaded');
所以不知何故,文件没有正确传递。我该怎么办?
【问题讨论】:
我可以看到您已将其置于测试模式。上传的文件是否大于 php.ini 中设置的最大文件大小? 不,不是这样,上传限制是2mb,测试文件是300kb。 【参考方案1】:对于遇到此问题的其他人,您现在可以这样做:
$response = $this->postJson('/product-import', [
'file' => new \Illuminate\Http\UploadedFile(resource_path('test-files/large-avatar.jpg'), 'large-avatar.jpg', null, null, null, true),
]);
更新
在Laravel 6中\Illuminate\Http\UploadedFile
类的构造函数有5个参数而不是6个。这是新的构造函数:
/**
* @param string $path The full temporary path to the file
* @param string $originalName The original file name of the uploaded file
* @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream
* @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
* @param bool $test Whether the test mode is active
* Local files are used in test mode hence the code should not enforce HTTP uploads
*
* @throws FileException If file_uploads is disabled
* @throws FileNotFoundException If the file does not exist
*/
public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false)
// ...
所以上面的解决方案就变得简单了:
$response = $this->postJson('/product-import', [
'file' => new \Illuminate\Http\UploadedFile(resource_path('test-files/large-avatar.jpg'), 'large-avatar.jpg', null, null, true),
]);
它对我有用。
【讨论】:
为了贡献,我添加了构造函数__construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false)
,它从上面的代码中调用。在我的单元测试中,它非常适用于图像和 PDF。
非常感谢,您结束了一个小时徒劳的搜索!【参考方案2】:
Docs for CrawlerTrait.html#method_action 读作:
参数 字符串 $method 字符串 $action 数组 $wildcards 数组 $parameters 数组 $cookies 数组 $files 数组 $server 字符串$内容
所以我认为正确的调用应该是
$response = $this->action(
'POST',
'FileStorageController@store',
[],
$values,
[],
['file' => $uploadedFile]
);
除非它需要非空通配符和 cookie。
【讨论】:
尝试您的建议。测试术语对我来说似乎很模糊:p 这是什么类型的测试? @JasperKennis,这不是行话,而是帮助人们在同一页面上讨论事物的术语。术语的滥用会造成混淆,而且会适得其反。您正在做的是功能测试或集成测试,具体取决于意图。根据经验,单元测试检查命令部分,即单个类的逻辑,模拟其他所有内容(合理)。集成测试检查声明部分,即配置、模板等。功能测试检查规范中定义的应用程序的功能。 这个工作^^测试仍然失败,但它超出了我被困在的指定点。请注意,我使用的是 Laravel 4.2,它不采用$wildcards
参数。
也谢谢你的解释,我把问题改成了功能测试。
这很奇怪。 $wildcards
从 v4.0 的第一次提交就在那里 github.com/laravel/framework/blob/… 没有 $cookies
,所以 $files
是第 5 个参数。无论如何,很高兴它有所帮助。【参考方案3】:
使用 phpunit,您可以使用 attach() 方法将文件附加到表单。
来自lumen docs的示例:
public function testPhotoCanBeUploaded()
$this->visit('/upload')
->name('File Name', 'name')
->attach($absolutePathToFile, 'photo')
->press('Upload')
->see('Upload Successful!');
【讨论】:
这没有帮助,我正在使用action
方法测试控制器响应。这是一个更高级别的示例。【参考方案4】:
最好最简单的方法:先导入必要的东西
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
然后制作一个假文件上传。
Storage::fake('local');
$file = UploadedFile::fake()->create('file.pdf');
然后制作一个 JSON 数据来传递文件。示例
$parameters =[
'institute'=>'Allen Peter Institute',
'total_marks'=>'100',
'aggregate_marks'=>'78',
'percentage'=>'78',
'year'=>'2002',
'qualification_document'=>$file,
];
然后将数据发送到您的 API。
$user = User::where('email','candidate@fakemail.com')->first();
$response = $this->json('post', 'api/user', $parameters, $this->headers($user));
$response->assertStatus(200);
我希望它会起作用。
【讨论】:
有没有办法伪造一个真实的 PDF 文件?我的store()
验证它是否真的是 PDF【参考方案5】:
这是一个如何使用自定义文件进行测试的完整示例。我需要它来解析已知格式的 CSV 文件,因此我的文件必须具有准确的格式和内容。如果您只需要图像或随机大小的文件,请使用 $file->fake->image() 或 create() 方法。这些与 Laravel 捆绑在一起。
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
class PanelistImportTest extends TestCase
/** @test */
public function user_should_be_able_to_upload_csv_file()
// If your route requires authenticated user
$user = Factory('App\User')->create();
$this->actingAs($user);
// Fake any disk here
Storage::fake('local');
$filePath='/tmp/randomstring.csv';
// Create file
file_put_contents($filePath, "HeaderA,HeaderB,HeaderC\n");
$this->postJson('/upload', [
'file' => new UploadedFile($filePath,'test.csv', null, null, null, true),
])->assertStatus(200);
Storage::disk('local')->assertExists('test.csv');
这是与之配套的控制器:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Storage;
class UploadController extends Controller
public function save(Request $request)
$file = $request->file('file');
Storage::disk('local')->putFileAs('', $file, $file->getClientOriginalName());
return response([
'message' => 'uploaded'
], 200);
【讨论】:
【参考方案6】:在您的测试用例中添加类似的setUp()
方法:
protected function setUp()
parent::setUp();
$_FILES = array(
'image' => array(
'name' => 'test.jpg',
'tmp_name' => __DIR__ . '/_files/phpunit-test.jpg',
'type' => 'image/jpeg',
'size' => 499,
'error' => 0
)
);
这会欺骗你的 $_FILES 全局,让 Laravel 认为有东西上传了。
【讨论】:
我试过这个,都将文件命名为image
或file
,但检查仍然失败。这应该与测试单元->action
方法一起使用吗?
您是否将实际大小和文件路径放入数组?还要尝试换行,这样 parent::setUp 就会在你欺骗你的 $_FILES 数组之后。
我更新了我的问题以反映这一点,但不,它没有帮助:(文件名和大小是正确的。以上是关于如何使用 laravel 和 phpunit 测试文件上传?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 API 中间件在 Laravel 中测试 PHPUnit 一个经过身份验证的用户?
如何使用 PHPUnit 在 Laravel 上测试更复杂的案例
如何创建仅用于测试的 Laravel 路由(phpunit)?