If you have a Symfony project that makes HTTP requests to your other projects (be it microservices, front-end and api separation projects, etc.), you may need to mock them for your tests.

This is a simple solution that allows you to create a mock custom class for the test environment in the symfony project, which can be used for your unit or functional tests.

The proposed solution is an alternative to using a library like http-mock, it may be more difficult to set up

Suppose we have an API Client class with methods, getResourceByPath($path) requests the API endpoint through a library (Guzzle, symfony's HttpClient, cURL or other) and returns the json we need

namespace AppBundle\Infra\Client;

class ApiClient
{
    public function getResourceByPath(string $path): array
    {
        $response = $this->httpClient->get($path);

        return json_decode($response->getBody());
    }
}

And this class should be declared in your config/services/client.yaml (for Symfony 4+ projects, or app/config.yml for Symfony 3)

services:
  AppBundle\Infra\Client\ApiClient: # (or app.client.api)
    public: true
    arguments: [...] # any arguments you need

Since we don't want to actually execute the request, we need to override the behavior of the method.

First, ApiClient needs an interface, if it doesn’t already exist

namespace AppBundle\Infra\Client;

interface ApiClientInterface
{
    public function getResourceByPath(string $path): array;
}

And edit the ApiClient class using the following command

class ApiClient implements ApiClientInterface {...}

Now we can create our mock class, which will perform any logic we need, such as retrieving the expected JSON from our local folder instead of the API

namespace tests\Mock\Client;

class ApiClientMock implements ApiClientInterface
{
    private $mockFolder = dirname(__DIR__).'/resources/api';

    public function getResourceByPath(string $path): array
    {
        $localFile = sprintf(
          '%s/%s.json',
          $this->mockFolder,
          $path
        );

        return json_decode(
            file_get_contents($localFile)
        );
    }
}

The structure of our test folder should look like this

tests/
  Mock/
    Client/
      ApiClientMock.php
    resources/
      api/
        article.json
        ....

You can now add mock statements in config/services_test.yaml (or app/config_test.yml)

services:
  AppBundle\Infra\Client\ApiClient: # (or app.client.api)
    class: Tests\Mocks\ApiClientMock

When running the test, you should now correctly retrieve the content from the local file

Of course, this example shows a simplified version of the logic, which you can customize to any needs. You may want to perform more verification on the called files and paths, or create a mapping between them and local files.

The system can also be used to simulate any other services that may call external services, such as notification services, mail programs, or any service that can write to the database, to avoid potentially sharing test data with the world

This solution also has an unexpected benefit: we can use a little trick to use mock local-if the test fails mysteriously and we need to debug; if we want to check the page rendering directly; or, if we want to deal with the API Future features not yet available: manually switch to the environment through test (by editing web/app_dev.php or .env), and use the content we retrieved locally to access the project on the browser

Likes(0)

Comment list count 0 Comments

No Comments