Home > Back-end >  How to Mock Aws\S3\S3Client for phpunit // how to mock magic methods
How to Mock Aws\S3\S3Client for phpunit // how to mock magic methods

Time:03-19

I need to mock the S3Client in my symfony5 project to be able to provoke exceptions and test the reaction of my code to those exceptions. We use aws/aws-sdk-php version 3.*

7 Years ago someone had the same problem and I tried to follow this solution [ PHPUnit - Mock S3Client not working well ] but I get errors.

What I do at the moment:

in my service:

class AwsS3Service implements AwsS3ServiceInterface
{
    private S3Client $s3Client;
    ....

    public function __construct(ParameterBagInterface $params)
    {
        [ ... ]

        $this->s3Client = $this->getS3client();
    }

    public function getS3client(): S3Client
    {
        return (new Sdk($this->config))->createS3();
    }

So I have a public method, which I can mock and make it to give me back a mocked S3Client.

in my tests I do the following:

        $this->awsS3Service = $this->createMock(AwsS3ServiceInterface::class);

        $command = $this->createMock(Command::class);
        /** @var S3Client $s3Client */
        $s3Client = $this->createMock(S3Client::class);

        $s3Client->method('listObjectsV2')
                 ->willThrowException(new S3Exception('VALIDATION ERROR', $command));
        $s3Client->method('putObject')
                 ->willThrowException(new S3Exception('VALIDATION ERROR', $command));
        $s3Client->method('getObject')
                 ->willThrowException(new S3Exception('VALIDATION ERROR', $command));
        $s3Client->method('deleteObject')
                 ->willThrowException(new S3Exception('VALIDATION ERROR', $command));

        $this->awsS3Service
            ->method('getS3client')
            ->willReturn($s3Client);

Now when I run the test I get the Error

Aws S3Service (App\Tests\Unit\Domain\AwsS3\AwsS3Service)
 ✘ Aws upload file s 3 exception
   ┐
   ├ PHPUnit\Framework\MockObject\MethodCannotBeConfiguredException: Trying to configure method "listObjectsV2" which cannot be configured because it does not exist, has not been specified, is final, or is static
   │
   ╵ /var/www/tests/Helper/AwsMockHelper.php:34
   ╵ /var/www/tests/Unit/Domain/AwsS3/AwsS3ServiceTest.php:35
   ┴

The problem seems to be, that the methods don't really exist in the S3Client class and are only somehow defined in the PhpDoc of the class:

 * @method \Aws\Result listObjectsV2(array $args = [])

We followed this documentation, when writing the service: https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-examples-creating-buckets.html

CodePudding user response:

As often it is easier to find a solution, when you know the name of your problem. I adapted the question title accordingly.

Methods defined in the header of a class, which don't really exist are called "magic methods" and with that knowledge I found a solution to my problem:

A comment under this link set me on the right track: https://carstenwindler.de/php/mocking-magic-methods-with-phpunit/

In the current version of phpunit, which is 9.5, you can use addMethods and this syntax to mock magic methods:

        $command = $this->createMock(Command::class);
        $s3Client = $this->getMockBuilder(S3Client::class)
                         ->disableOriginalConstructor()
                         ->addMethods(['listObjectsV2', 'putObject', 'getObject', 'deleteObject'])
                         ->getMock();

        $s3Client->expects($this->once())
                 ->method('listObjectsV2')
                 ->willThrowException(new AwsException('VALIDATION ERROR', $command));
        $s3Client->expects($this->once())
                 ->method('putObject')
                 ->willThrowException(new AwsException('VALIDATION ERROR', $command));
        $s3Client->expects($this->once())
                 ->method('getObject')
                 ->willThrowException(new AwsException('VALIDATION ERROR', $command));
        $s3Client->expects($this->once())
                 ->method('deleteObject')
                 ->willThrowException(new AwsException('VALIDATION ERROR', $command));
  • Related