简体   繁体   中英

TypeError: Argument 1 passed to PHPUnit\Framework\Assert::assertFileExists() must be of the type string, null given,

I have written the following test in Laravel:

public function testUserCanUploadFile()
    {
        $this->withoutExceptionHandling();
        $user = $this->signIn();

        Storage::fake('public'); //Mock a disk
        $file = UploadedFile::fake()->image('test.jpg'); //Upload a fake image.

        $assortmentAttributes = Assortment::factory()->raw(); // Use the assortment factory.
        $assortmentAttributes['image_path'] = $file; // Add a additional field in the assortment factory.
    
        $this->post(route('assortments.store'), $assortmentAttributes)->assertRedirect(); // Post the fields to the assortmentcontroller store method.
        //Storage::disk('public')->assertExists($file->hashName()); // Check if the field exists.
        $this->assertFileExists($user->image_path);
    }

I want to check if the file also exists in the database. In practice, the file upload works just fine. My test however, does not.

When I run my test I get the following error: TypeError: Argument 1 passed to PHPUnit\Framework\Assert::assertFileExists() must be of the type string, null given

The field is not set on ->nullable() in my migration. I also did dd($user->image_path) . This just returns null .

I also changed: $assortmentAttributes['image'] = $file; // Add a additional field in the assortment factory. $assortmentAttributes['image'] = $file; // Add a additional field in the assortment factory.

To: $assortmentAttributes['image_path'] = $file; // Add a additional field in the assortment factory. $assortmentAttributes['image_path'] = $file; // Add a additional field in the assortment factory.

To make sure the field name matches the field name in my assortment migration. Does anyone know how I can make sure that the file is found in my database field, so that my test finally works?

The code in my store method in my controller:

if ($request->hasFile('image')) {
            $image = $request->file('image'); //request the file
            $fileName = md5_file($image . microtime()) . '.' . $image->getClientOriginalExtension(); //use md5 for security reasons and get the extension.
            $image->storeAs('', $fileName, 'public'); //store the file in the public folder disk.
        } 
        
         if ($request->wantsJson()) {
             return response([], 204);
        }

What signIn() does:

protected function signIn($user = null)
    {
        $user = $user ?: User::factory()->create();

        $this->actingAs($user);

        return $user;
    }

Greetings,

Parsa

Based on my comment here you are creating a user of fly and it's not persisted so your endpoint assortments.store does not know anything about it. You are mixing different kinds of testing approaches.

There are a few issues you need to address:

  1. Decide what kind of testing (unit/integration/functional) you are doing, based on this answer you can decide what to do next.
  2. If it's unit testing, then you cannot make POST request, because unit-testing is about testing a single unit from a class (eg: one method)
  3. If it's integration testing, then you can "integrate" systems around your service eg: you want to test that your "service" foo can call service bar using the latest Laravel
  4. If it's functional testing (most complicated and expensive), you'll need to set up a "testing environment" will all dependencies as MySQL, Redis and etc.

Probably the best way for you to tackle your specific problem : you need to call an action which is responsible for "assortments.store" directly from your test omitting Laravel Http Stack

Your code MAY looks like:

public function testUserCanUploadFile()
    {
        $this->withoutExceptionHandling();
        $user = $this->signIn();
        // saving fake storage into variable for later
        $storage = Storage::fake('public'); // Mock a disk 
        $file = UploadedFile::fake()->image('test.jpg'); //Upload a fake image.

        $assortmentAttributes = Assortment::factory()->raw(); // Use the assortment factory.
        $assortmentAttributes['image_path'] = $file; // Add a additional field in the assortment factory.
        $assortmentAttributes['user'] = $user; // passing user with request to controller

        // init controller and pass fake "storage" as dependencies (that's probably you have it)
        $controller = new AssortmentStoreController($storage);
        $request = new Request($assortmentAttributes);
        $response = $controller->handleAction($request); // calling action
    
        // Assert your response

        // Assert your fake storage

        // Assert user image because it was changed by object reference.
        $this->assertFileExists($user->image_path);
    }

I would do dd($user);

Just after: $user = $this->signIn();

Probably you're getting wrong data in user .

It would be helpful to see exactly what your User model looks like, but I think you just need to pull the updated user back from the database. Your current $user variable will not contain the updated value because you haven't asked for it.

Try changing your assertion to:

$this->assertFileExists($user->fresh()->image_path);

fresh will return a new instance of the model with the latest data from the database.


To update your user in your controller:

$request->user()->update(['image_path' => $fileName]);

When $user->image_path is null, file does not exist. You will receive failed test results. So, I suggest one line where you verify $user->image_path value that is null or not.

$this->assertNotNull($user->image_path);
$this->assertFileExists($user->image_path);

I noticed in your controller you are checking ($request->hasFile('image')) While in the test you are sending $assortmentAttributes['image_path'] = $file; // Add a additional field in the assortment factory. $assortmentAttributes['image_path'] = $file; // Add a additional field in the assortment factory.

You should instead update the test to

$assortmentAttributes['image'] = $file; // Add a additional field in the assortment factory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM