Captura de argumentos em testes PHP
Tags: braziliandevs, php, webdev, testing
Published: 2021-10-23 08:10:30

Você já está se sentindo confiante com suas habilidades de teste e os seus colegas te invejam ao ouvir você falar de mocks e stubs. Mas agora, um novo desafio surge e você só pode verificar certas propriedades de um argumento, caso contrário seus testes falharão miseravelmente.

![Gif mostrando alguém horrorizado](https://media.giphy.com/media/1iUZa41YxKQtaJq0/giphy.gif)


## Um novo dia começa
Seu trabalho era implementar uma simples funcionalidade de cadastro de usuário. Como uma boa programadora, você planeja usar o padrão `Repositório` para poder separar a camada de persistência e mocká-la em testes unitários caso necessário. 

![Gif de alguém pensando que isso vai ser fácil](https://media.giphy.com/media/kzs7srxbKNqjbI62U4/giphy.gif)

### Classe usuário
Não poderia ser mais simples, certo?
```php
final class User
{
    public function __construct(
        private string $id,
        private string $name)
    {
    }

    public function getId(): string
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }
}
```

### Repositório da entidade usuário
Não está persistindo de fato, mas vamos fingir que sim.

```php
final class UserRepository
{
    public function save(User $user): void
    {
        echo sprintf('Persistindo usuário com id %s e nome %s', $user->getId(), $user->getName());
    }
}
```

### Classe UserService
Agora temos uma reviravolta! Um requisito maluco diz que o campo `id` deve ser randômico. Bom, temos várias maneiras de fazer isso, então vamos prosseguir:
```php
final class UserService
{
    public function __construct(private UserRepository $repository) { }

    public function createNewUser(string $name): void
    {
        $user = new User(uniqid(), $name);

        $this->repository->save($user);
    }
}
```

Legal, tudo está funcionando.

## Não vamos nos esquecer dos testes
![Gif de alguém lembrando de testar](https://media.giphy.com/media/3o6Mbbs879ozZ9Yic0/giphy.gif)

Nós queremos verificar quando `UserService` cria um `User`, se o `UserRepostory` está sendo chamado corretamente. Não nos importamos com a persistência agora, então podemos mockar o repositório e tudo deve funcionar.
```php
public function testFailing(): void
{
    $repository = $this->createMock(UserRepository::class);

    $userService = new UserService($repository);

    $repository
        ->expects($this->once())
        ->method('save')
        ->with(new User('id não randômico', 'user'));

    $userService->createNewUser('user');
}
```
Infelizmente isso não funciona. O teste falha porque `User` com o id `id não randômico` não corresponde ao argumento passado pelo `UserRepository` que tem um id diferente cada vez que um novo usuário é criado.

## Captura de argumento ao resgate!
No Java, nós temos essa mesa funcionalidade provida pelo [Mockito](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#15):

> `Captura de argumento` possibilita a verificação de apenas alguns valores do argumento ao invés de testar a equalidade do objeto inteiro.

No nosso exemplo, nós não nos precisamos testar realmente o valor do `id` porque ele é gerado randomicamente pelo próprio PHP através da função `uniqid`.

No final o teste usando `captura de argumento` fica assim
```php
public function testRepositoryShouldCreateUserWithCorrectName(): void
{
    $repository = $this->createMock(UserRepository::class);

    $userService = new UserService($repository);

    $repository
        ->expects($this->once())
        ->method('save')
        ->will($this->returnCallback(function($user) {
            self::assertEquals('user', $user->getName());
        }));

    $userService->createNewUser('user');
}
```

Obrigado por ler até aqui, espero que tenha gostado e boa sorte com seus testes!

![Good luck!](https://media.giphy.com/media/j1Xyt3DHfJcmk/giphy.gif)