Asserções
As asserções são usadas para confirmar que o valor real corresponde ao valor esperado. São métodos da
classe Tester\Assert
.
Escolha as asserções mais adequadas. É melhor Assert::same($a, $b)
do que Assert::true($a === $b)
,
porque em caso de falha exibe uma mensagem de erro significativa. No segundo caso, apenas false should be true
,
o que não nos diz nada sobre o conteúdo das variáveis $a
e $b
.
A maioria das asserções também pode ter uma descrição opcional no parâmetro $description
, que será exibida
na mensagem de erro se a expectativa falhar.
Os exemplos pressupõem a criação de um alias:
use Tester\Assert;
Assert::same ($expected, $actual, ?string $description=null)
$expected
deve ser idêntico a $actual
. O mesmo que o operador PHP ===
.
Assert::notSame ($expected, $actual, ?string $description=null)
O oposto de Assert::same()
, ou seja, o mesmo que o operador PHP !==
.
Assert::equal ($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false)
$expected
deve ser igual a $actual
. Ao contrário de Assert::same()
, a identidade dos
objetos, a ordem dos pares chave ⇒ valor em arrays e números decimais marginalmente diferentes são ignorados, o que pode ser
alterado definindo $matchIdentity
e $matchOrder
.
Os seguintes casos são idênticos do ponto de vista de equal()
, mas não de same()
:
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
['first' => 11, 'second' => 22],
['second' => 22, 'first' => 11],
);
No entanto, atenção, os arrays [1, 2]
e [2, 1]
não são iguais, porque diferem apenas na ordem dos
valores, não nos pares chave ⇒ valor. O array [1, 2]
também pode ser escrito como
[0 => 1, 1 => 2]
e, portanto, [1 => 2, 0 => 1]
será considerado igual.
Além disso, em $expected
pode-se usar as chamadas expectations.
Assert::notEqual ($expected, $actual, ?string $description=null)
O oposto de Assert::equal()
.
Assert::contains ($needle, string|array $actual, ?string $description=null)
Se $actual
for uma string, deve conter a substring $needle
. Se for um array, deve conter o elemento
$needle
(comparado estritamente).
Assert::notContains ($needle, string|array $actual, ?string $description=null)
O oposto de Assert::contains()
.
Assert::hasKey (string|int $needle, array $actual, ?string $description=null)
$actual
deve ser um array e deve conter a chave $needle
.
Assert::notHasKey (string|int $needle, array $actual, ?string $description=null)
$actual
deve ser um array e não deve conter a chave $needle
.
Assert::true ($value, ?string $description=null)
$value
deve ser true
, ou seja, $value === true
.
Assert::truthy ($value, ?string $description=null)
$value
deve ser verdadeiro, ou seja, satisfaz a condição if ($value) ...
.
Assert::false ($value, ?string $description=null)
$value
deve ser false
, ou seja, $value === false
.
Assert::falsey ($value, ?string $description=null)
$value
deve ser falso, ou seja, satisfaz a condição if (!$value) ...
.
Assert::null ($value, ?string $description=null)
$value
deve ser null
, ou seja, $value === null
.
Assert::notNull ($value, ?string $description=null)
$value
não deve ser null
, ou seja, $value !== null
.
Assert::nan ($value, ?string $description=null)
$value
deve ser Not a Number. Para testar valores NAN, use exclusivamente Assert::nan()
. O valor NAN
é muito específico e as asserções Assert::same()
ou Assert::equal()
podem funcionar
inesperadamente.
Assert::count ($count, Countable|array $value, ?string $description=null)
O número de elementos em $value
deve ser $count
. Ou seja, o mesmo que
count($value) === $count
.
Assert::type (string|object $type, $value, ?string $description=null)
$value
deve ser do tipo especificado. Como $type
, podemos usar uma string:
array
list
– array indexado por uma sequência crescente de chaves numéricas a partir de zerobool
callable
float
int
null
object
resource
scalar
string
- nome da classe ou diretamente o objeto, então
$value instanceof $type
deve ser verdadeiro
Assert::exception (callable $callable, string $class, ?string $message=null, $code=null)
Ao chamar $callable
, uma exceção da classe $class
deve ser lançada. Se especificarmos
$message
, a mensagem da exceção também deve corresponder ao padrão e, se
especificarmos $code
, os códigos também devem corresponder estritamente.
O seguinte teste falhará porque a mensagem da exceção não corresponde:
Assert::exception(
fn() => throw new App\InvalidValueException('Zero value'),
App\InvalidValueException::class,
'Value is to low',
);
Assert::exception()
retorna a exceção lançada, permitindo testar também exceções aninhadas.
$e = Assert::exception(
fn() => throw new MyException('Something is wrong', 0, new RuntimeException),
MyException::class,
'Something is wrong',
);
Assert::type(RuntimeException::class, $e->getPrevious());
Assert::error (string $callable, int|string|array $type, ?string $message=null)
Verifica se a função $callable
gerou os erros esperados (ou seja, avisos, notices, etc.). Como
$type
, especificamos uma das constantes E_...
, por exemplo, E_WARNING
. E se especificarmos
$message
, a mensagem de erro também deve corresponder ao padrão. Por exemplo:
Assert::error(
fn() => $i++,
E_NOTICE,
'Undefined variable: i',
);
Se o callback gerar múltiplos erros, devemos esperá-los todos na ordem exata. Nesse caso, passamos um array em
$type
:
Assert::error(function () {
$a++;
$b++;
}, [
[E_NOTICE, 'Undefined variable: a'],
[E_NOTICE, 'Undefined variable: b'],
]);
Se você especificar um nome de classe como $type
, ele se comporta da mesma forma que
Assert::exception()
.
Assert::noError (callable $callable)
Verifica se a função $callable
não gerou nenhum aviso, erro ou exceção. É útil para testar pedaços de
código onde não há outra asserção.
Assert::match (string $pattern, $actual, ?string $description=null)
$actual
deve corresponder ao padrão $pattern
. Podemos usar duas variantes de padrões: expressões
regulares ou caracteres curinga.
Se passarmos uma expressão regular como $pattern
, devemos usar ~
ou #
para
delimitá-la, outros delimitadores não são suportados. Por exemplo, um teste onde $var
deve conter apenas dígitos
hexadecimais:
Assert::match('#^[0-9a-f]$#i', $var);
A segunda variante é semelhante à comparação normal de strings, mas em $pattern
podemos usar vários
caracteres curinga:
%a%
um ou mais caracteres, exceto caracteres de fim de linha%a?%
nenhum ou mais caracteres, exceto caracteres de fim de linha%A%
um ou mais caracteres, incluindo caracteres de fim de linha%A?%
nenhum ou mais caracteres, incluindo caracteres de fim de linha%s%
um ou mais espaços em branco, exceto caracteres de fim de linha%s?%
nenhum ou mais espaços em branco, exceto caracteres de fim de linha%S%
um ou mais caracteres, exceto espaços em branco%S?%
nenhum ou mais caracteres, exceto espaços em branco%c%
qualquer caractere único, exceto o caractere de fim de linha%d%
um ou mais dígitos%d?%
nenhum ou mais dígitos%i%
valor inteiro com sinal%f%
número de ponto flutuante%h%
um ou mais dígitos hexadecimais%w%
um ou mais caracteres alfanuméricos%%
o caractere %
Exemplos:
# Novamente, teste para número hexadecimal
Assert::match('%h%', $var);
# Generalização do caminho do arquivo e número da linha
Assert::match('Error in file %a% on line %i%', $errorMessage);
Assert::matchFile (string $file, $actual, ?string $description=null)
A asserção é idêntica a Assert::match(), mas o padrão é carregado do arquivo
$file
. Isso é útil para testar strings muito longas. O arquivo com o teste permanecerá claro.
Assert::fail (string $message, $actual=null, $expected=null)
Esta asserção sempre falha. Às vezes, isso é útil. Opcionalmente, podemos especificar também o valor esperado e o atual.
Expectations
Quando queremos comparar estruturas mais complexas com elementos não constantes, as asserções acima podem não ser
suficientes. Por exemplo, testamos um método que cria um novo usuário e retorna seus atributos como um array. Não conhecemos
o valor do hash da senha, mas sabemos que deve ser uma string hexadecimal. E sobre outro elemento, sabemos apenas que deve ser um
objeto DateTime
.
Nessas situações, podemos usar Tester\Expect
dentro do parâmetro $expected
dos métodos
Assert::equal()
e Assert::notEqual()
, com os quais a estrutura pode ser facilmente descrita.
use Tester\Expect;
Assert::equal([
'id' => Expect::type('int'), # esperamos um número inteiro
'username' => 'milo',
'password' => Expect::match('%h%'), # esperamos uma string que corresponda ao padrão
'created_at' => Expect::type(DateTime::class), # esperamos uma instância da classe
], User::create(123, 'milo', 'RandomPaSsWoRd'));
Com Expect
, podemos realizar quase as mesmas asserções que com Assert
. Ou seja, temos à
disposição os métodos Expect::same()
, Expect::match()
, Expect::count()
, etc. Além
disso, podemos encadeá-los:
Expect::type(MyIterator::class)->andCount(5); # esperamos MyIterator e número de elementos 5
Ou podemos escrever nossos próprios manipuladores de asserções.
Expect::that(function ($value) {
# retornamos false se a expectativa falhar
});
Investigando asserções falhas
Quando uma asserção falha, o Tester exibe onde está o erro. Se compararmos estruturas mais complexas, o Tester criará
dumps dos valores comparados e os salvará no diretório output
. Por exemplo, em caso de falha do teste fictício
Arrays.recursive.phpt
, os dumps serão salvos da seguinte forma:
app/
└── tests/
├── output/
│ ├── Arrays.recursive.actual # valor atual
│ └── Arrays.recursive.expected # valor esperado
│
└── Arrays.recursive.phpt # teste falho
O nome do diretório pode ser alterado através de Tester\Dumper::$dumpDir
.