Back to Course |
Testing in Laravel 11: Advanced Level

Testing File Uploads with Storage Fake

Let's talk about testing the file uploads and checking various things:

  • Whether the file upload was successful
  • Whether the file is where it belongs
  • Whether the file name is assigned successfully.

Like a few other "external" things in Laravel, file uploads need to be faked for testing.

So, you don't work with the actual file uploads to the real storage. You fake the Storage driver, then Laravel, with its testing mechanism, puts those files elsewhere in a separate temporary folder, and then you assert whether that Storage temporary fake disk contains the file.


Scenario Example

For example, you have a Controller to store a product, and one of the fields is photo. You store the photo in the products folder and save the filename as a string to the photo database field.

use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
 
class ProductController extends Controller
{
// ...
 
public function store(Request $request): RedirectResponse
{
$product = Product::create($request->all());
 
if ($request->hasFile('photo')) {
$filename = $request->file('photo')->getClientOriginalName();
$request->file('photo')->storeAs('products', $filename);
$product->update(['photo' => $filename]);
}
 
return redirect()->route('products.index');
}
 
// ...
}

Test

The start of the test is the same as many tests, acting as some user posting to the route.

use function Pest\Laravel\actingAs;
 
beforeEach(function () {
$this->user = User::factory()->create();
});
 
test('product create photo upload successful', function () {
actingAs($this->user)
->post('/products', [
'name' => 'Product 123',
'price' => 1234,
'photo' => '',
]);
 
// asserts
});

Now, how do you pass the photo, and what do you assert? First, we must use the Storage facade, and we will call the fake() method from it. If you have created a specific disk, it can be passed through the fake() method.

For the photo, you must call the fake() method from the UploadedFile facade. You can call the image() method from there and pass in parameters such as the filename.

use Illuminate\Http\UploadedFile;
use function Pest\Laravel\actingAs;
use Illuminate\Support\Facades\Storage;
 
beforeEach(function () {
$this->user = User::factory()->create();
});
 
test('product create photo upload successful', function () {
Storage::fake();
 
actingAs($this->user)
->post('/products', [
'name' => 'Product 123',
'price' => 1234,
'photo' => UploadedFile::fake()->image('photo1.jpg'),
]);
 
// asserts
});

Now, how do we assert? We need to make two assertions: first, the file exists in the storage, and second, the correct filename is saved to the database.

We can use assertExists() from the same Storage facade for the storage assertion.

use App\Models\User;
use App\Models\Product;
use Illuminate\Http\UploadedFile;
use function Pest\Laravel\actingAs;
use Illuminate\Support\Facades\Storage;
 
beforeEach(function () {
$this->user = User::factory()->create();
});
 
test('product create photo upload successful', function () {
Storage::fake();
 
actingAs($this->user)
->post('/products', [
'name' => 'Product 123',
'price' => 1234,
'photo' => UploadedFile::fake()->image('photo1.jpg'),
]);
 
expect(Product::latest()->first()->photo)
->toBe('photo1.jpg');
 
Storage::assertExists('products/photo1.jpg');
});