Reputation: 674
I am building a file upload JSON API with Lumen and trying to write phpunit API tests.
The problem I am having though is that as soon as I try to simulate a file upload with a real image constructed like ["file" => new UploadedFile(TestUtils::tempPath('test.jpg'), 'test.jpg', "image\jpeg", 100, null, true)]
and sending it via $this->json('POST', '/chunk', $data);
,
I can't do anything with the uploaded file because it is an array and I get the error Call to a member function move() on array in ...
Oddly enough if I use UploadedFile::fake()->image('test.jpg')
it mangles my request and the other data I send with it isn't available in the controller.
If I dd the file object in my controller, it shows an "array(0){}". I am thinking it has something to do with the transfer via json('POST'), as unit tests that use an UploadedFileObject directly run successfully.
If I dd the entire request, it shows:
["request"]=>
object(Symfony\Component\HttpFoundation\ParameterBag)#52 (1) {
["parameters":protected]=>
array(5) {
["chunkType"]=>
string(4) "flow"
["flowIdentifier"]=>
string(13) "Testing123Bnu"
["flowChunkNumber"]=>
int(1)
["flowTotalChunks"]=>
int(2)
["file"]=>
array(0) {
}
}
}
...
["files"]=>
object(Symfony\Component\HttpFoundation\FileBag)#71 (1) {
["parameters":protected]=>
array(0) {
}
}
...
["content":protected]=> string(103) "{(otherData..), "file":{}}"
I have no idea how to circumvent this or test a file upload in this situation and have been searching for hours. The symfony test mode in the UploadedFile constructor has no effect.
Here is my test:
public function testChunkUpload_FlowChunk()
{
$data = [ (otherData)
"file" => new UploadedFile(TestUtils::tempPath('test.jpg'), 'test.jpg', "image\jpeg", 100, null, true)
//UploadedFile::fake()->image('test.jpg')
];
$this->assertFileExists(TestUtils::tempPath('test.jpg'));
$this->json('POST', '/chunk', $data);
$this->assertEquals(200, $this->response->status());
$this->assertEquals('application/json', $this->response->headers->get('Content-Type'));
}
And here is the relevant method in my controller:
public function receiveChunk(Request $request){
if (! $request->has('chunkType')) {
return response()->json(["Invalid Chunk Type"], 400);
}
$data = $this->chunkNormalizer->normalizeInput($request->all());
$chunk = $this->chunkFactory->createChunk( (otherData)
$data['fileHandle']);
$this->fileAssembler->processChunk($chunk);
return response()->json(["Success"], 200);
}
And the error occurs in processChunk:
public function processChunk(FileChunk $chunk){
...
$chunkName = "..." . ".chunk";
$fileHandle = $chunk->getFileHandle();
$fileHandle->move($this->assemblePath, $chunkName);
}
Any ideas would be greatly appreciated!
Upvotes: 0
Views: 965
Reputation: 1188
this is working for me. just simple. this can submit data (name and email) and file upload on the same route.
1.condition without authentication
5*1000
is file size (in KB). so i made a test with 5 MB
file.
use Faker\Factory as Faker;
use Illuminate\Http\UploadedFile;
class SampleTest extends TestCase
{
public function testItCanCreateUser()
{
$faker = Faker::create();
$data = array(
'name' => $faker->name
);
$files = [
'file' => UploadedFile::fake()->create('file.jpg', 5*1000)
];
$response = $this->call('POST', '/chunk', $data, [], $files);
$this->assertEquals(200, $response->getStatusCode());
}
}
2.condition with authentication (logged in user)
use Faker\Factory as Faker;
use Illuminate\Http\UploadedFile;
class SampleTest extends TestCase
{
public function testItCanUpdateProfileUser()
{
$faker = Faker::create();
$data = array(
'name' => $faker->name
);
$files = [
'file' => UploadedFile::fake()->create('file.jpg', 5*1000)
];
$headers = [
'Accept' => 'application/json',
'Authorization' => 'your-jwt-token'
];
$servers = [];
foreach ($headers as $k => $header) {
$servers["HTTP_" . $k] = $header;
}
$response = $this->call('POST', '/chunk', $data, [], $files, $servers);
$this->assertEquals(200, $response->getStatusCode());
}
}
you need to add HTTP_
on every request header. i'm not sure why. but it will work.
Upvotes: 0
Reputation: 674
EDIT: Strangely enough, in the laravel docs the "json" method is used instead of "call" as well. Could this be an error? Or just a Lumen incompatability issue? (Laravel Docs)
I have figured out how to get the test to run. My assumptions about how the file is uploaded through the JS vendor library were wrong. In fact, the file chunks are uploaded with a XmlHttpRequest "multipart/formdata" and NOT as JSON.
I understood the error (JSON encoding), but not the source. After understanding that it uses an XmlHttpRequest, the fix was simple.
Working test:
public function testChunkUpload_FlowChunk()
{
$data = [ (otherData)
"file" => new UploadedFile(TestUtils::tempPath('test.jpg'), 'test.jpg', "image\jpeg", 100, null, true)
//UploadedFile::fake()->image('test.jpg')
];
$this->assertFileExists(TestUtils::tempPath('test.jpg'));
$this->call('POST', '/chunk', $data);
$this->assertEquals(200, $this->response->status());
$this->assertEquals('application/json', $this->response->headers->get('Content-Type'));
}
I am still unsure about why using UploadedFile::fake()->image("xyz.jpg") had a different effect, now the test runs with both methods.
Upvotes: 0