-
Notifications
You must be signed in to change notification settings - Fork 0
Testing
How to test code that uses initphp/upload without hitting a real network or
clobbering real files.
The local adapter works entirely on the filesystem, so the easiest end-to-end
test points it at a throwaway directory and feeds it a file via
File::setPath() (which
copies, leaving your fixture intact):
use InitPHP\Upload\Upload;
use InitPHP\Upload\File;
use InitPHP\Upload\Adapters\LocalAdapter;
use PHPUnit\Framework\TestCase;
final class AvatarUploadTest extends TestCase
{
public function testStoresAvatar(): void
{
$dir = sys_get_temp_dir() . '/upload-test-' . uniqid();
mkdir($dir);
$source = $dir . '/source.png';
file_put_contents($source, 'fake-png-bytes');
$upload = new Upload(new LocalAdapter([
'dir' => $dir . '/store',
'url' => 'https://cdn.test',
]));
$stored = $upload->setFile(File::setPath($source))->to('avatars');
self::assertNotFalse($stored);
self::assertFileExists($dir . '/store/avatars/source.png');
self::assertSame('https://cdn.test/avatars/source.png', $stored->getURL());
}
}File::setPost() reads the $_FILES superglobal, so you can drive it directly
in a test. Back it up and restore it so tests stay isolated:
protected function setUp(): void
{
$this->filesBackup = $_FILES;
$_FILES = [];
}
protected function tearDown(): void
{
$_FILES = $this->filesBackup;
}
public function testNormalizesMultiFileField(): void
{
$_FILES['gallery'] = [
'name' => ['one.png', 'two.gif'],
'type' => ['image/png', 'image/gif'],
'tmp_name' => ['/tmp/php1', '/tmp/php2'],
'error' => [UPLOAD_ERR_OK, UPLOAD_ERR_OK],
'size' => [100, 200],
];
$files = File::setPost('gallery');
self::assertCount(2, $files);
self::assertSame('two.gif', $files[1]->getName());
}
move_uploaded_file()only succeeds for genuine HTTP uploads, so a$_FILES-driven test with faketmp_names cannot exercise the local adapter's move branch. UseFile::setPath()(the copy branch) for end-to-end storage assertions, as shown above.
Both adapters expose protected seams you can override in a subclass, so you can test your upload logic without a live server or the AWS SDK.
FTPAdapter performs the transfer through two protected methods —
prepareDirectory(string $remoteName) and upload(string $remoteName, string $localPath): bool. Override them to record calls:
use InitPHP\Upload\Adapters\FTPAdapter;
final class FakeFTPAdapter extends FTPAdapter
{
public array $uploaded = [];
public bool $result = true;
protected function prepareDirectory(string $remoteName): void
{
// no-op: no real connection
}
protected function upload(string $remoteName, string $localPath): bool
{
$this->uploaded[] = $remoteName;
return $this->result;
}
}
// In a test (skip when ext-ftp is absent):
$adapter = new FakeFTPAdapter(['url' => 'https://cdn.test']);
$stored = (new Upload($adapter))->setFile($file)->to('gallery');
self::assertSame(['gallery/' . $file->getName()], $adapter->uploaded);FakeFTPAdapter's constructor still requires ext-ftp; guard the test with
if (!extension_loaded('ftp')) self::markTestSkipped(...).
S3Adapter gets its client through a protected getClient(): \Aws\S3\S3Client.
Override it to return a recording fake, so you can assert the bucket, key and
ACL the adapter builds:
use InitPHP\Upload\Adapters\S3Adapter;
final class FakeS3Adapter extends S3Adapter
{
public array $lastArgs = [];
protected function getClient(): \Aws\S3\S3Client
{
return new class($this) extends \Aws\S3\S3Client {
public function __construct(private FakeS3Adapter $owner) {}
public function putObject(array $args)
{
$this->owner->lastArgs = $args;
return new \Aws\Result(['ObjectURL' => 'https://s3.test/' . $args['Key']]);
}
};
}
}
$adapter = new FakeS3Adapter([
'bucket' => 'my-bucket', 'region' => 'eu-central-1',
'key' => 'k', 'secret_key' => 's',
]);
$stored = (new Upload($adapter))->setFile($file)->to('avatars');
self::assertSame('my-bucket', $adapter->lastArgs['Bucket']);
self::assertSame('avatars/' . $file->getName(), $adapter->lastArgs['Key']);(The S3 adapter's constructor requires the aws/aws-sdk-php classes to exist.)
initphp/upload ships a full PHPUnit suite that uses exactly these techniques:
temp-directory tests for the local adapter, $_FILES manipulation for
setPost(), and seam-overriding test doubles for FTP and S3 (the AWS classes
are stubbed so the SDK is not required in CI). Run it with:
composer test # PHPUnit
composer ci # PHP-CS-Fixer + PHPStan (max) + PHPUnit-
The
FileObject —setPost()/setPath() - Recipes — the code you would be testing
-
API Reference — the seams (
BaseUploadAdapter)
initphp/upload · MIT License · part of the InitPHP family
Source · Issues · Discussions · Packagist · Contributing · Security Policy
Getting Started
Reference
Adapters
Practical Guides
Other