Afirmaciones
Las aserciones se utilizan para afirmar que un valor real coincide con un valor esperado. Son métodos de
Tester\Assert
.
Elija las aserciones más precisas. Es mejor Assert::same($a, $b)
que Assert::true($a === $b)
porque
muestra un mensaje de error significativo en caso de fallo. En el segundo caso sólo obtenemos false should be true
y
no dice nada sobre el contenido de las variables $a y $b.
La mayoría de las aserciones también pueden tener un $description
opcional que aparece en el mensaje de error si
la expectativa falla.
Los ejemplos asumen que el siguiente alias de clase está definido:
use Tester\Assert;
Assert::same($expected, $actual, ?string $description=null)
$expected
debe ser el mismo que $actual
. Es el mismo que el operador PHP ===
.
Assert::notSame($expected, $actual, ?string $description=null)
Opuesto a Assert::same()
, por lo que es el mismo que el operador PHP !==
.
Assert::equal($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false)
$expected
debe ser igual a $actual
. A diferencia de Assert::same()
, se ignoran la
identidad del objeto, el orden de los pares clave ⇒ valor en matrices, y los números decimales marginalmente diferentes, que
pueden cambiarse configurando $matchIdentity
y $matchOrder
.
Los siguientes casos son idénticos desde el punto de vista de equal()
, pero no para same()
:
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
['primero' => 11, 'segundo' => 22],
['segundo' => 22, 'primero' => 11],
);
Sin embargo, cuidado, la matriz [1, 2]
y [2, 1]
no son iguales, porque sólo difiere el orden de los
valores, no los pares clave ⇒ valor. El array [1, 2]
también puede escribirse como
[0 => 1, 1 => 2]
y por tanto [1 => 2, 0 => 1]
se considerará igual.
También puede utilizar las llamadas expectativas en $expected
.
Assert::notEqual($expected, $actual, ?string $description=null)
Opuesto a Assert::equal()
.
Assert::contains($needle, string|array $actual, ?string $description=null)
Si $actual
es una cadena, debe contener la subcadena $needle
. Si es una matriz, debe contener el
elemento $needle
(se compara estrictamente).
Assert::notContains($needle, string|array $actual, ?string $description=null)
Opuesto a Assert::contains()
.
Assert::hasKey(string|int $needle, array $actual, ?string $description=null)
$actual
debe ser un array y debe contener la clave $needle
.
Assert::notHasKey(string|int $needle, array $actual, ?string $description=null)
$actual
debe ser una matriz y no debe contener la clave $needle
.
Assert::true($value, ?string $description=null)
$value
debe ser true
, por lo que $value === true
.
Assert::truthy($value, ?string $description=null)
$value
debe ser verdadero, por lo que satisface la condición if ($value) ...
.
Assert::false($value, ?string $description=null)
$value
debe ser false
, por lo que $value === false
.
Assert::falsey($value, ?string $description=null)
$value
debe ser falsa, por lo que satisface la condición if (!$value) ...
.
Assert::null($value, ?string $description=null)
$value
debe ser null
, por lo que $value === null
.
Assert::notNull($value, ?string $description=null)
$value
no debe ser null
, entonces $value !== null
.
Assert::nan($value, ?string $description=null)
$value
debe ser Not a Number. Utilice únicamente Assert::nan()
para las pruebas NAN. El valor NAN es
muy específico y las aserciones Assert::same()
o Assert::equal()
pueden comportarse de forma
impredecible.
Assert::count($count, Countable|array $value, ?string $description=null)
El número de elementos en $value
debe ser $count
. Por tanto, igual que
count($value) === $count
.
Assert::type(string|object $type, $value, ?string $description=null)
$value
debe ser de un tipo determinado. Como $type
podemos utilizar string:
array
list
– array indexado en orden ascendente de claves numéricas desde cerobool
callable
float
int
null
object
resource
scalar
string
- nombre de clase u objeto directamente entonces debe pasar
$value instanceof $type
Assert::exception(callable $callable, string $class, ?string $message=null, $code=null)
Al invocar $callable
debe lanzarse una excepción de instancia $class
. Si pasamos
$message
, el mensaje de la excepción debe coincidir. Y si pasamos
$code
, código de la excepción debe ser el mismo.
Por ejemplo, esta prueba falla porque el mensaje de la excepción no coincide:
Assert::exception(
fn() => throw new App\InvalidValueException('Valor cero'),
App\InvalidValueException::class,
'El valor es demasiado bajo',
);
El Assert::exception()
devuelve una excepción lanzada, por lo que puede probar una excepción anidada.
$e = Assert::exception(
fn() => throw new MyException('Algo va mal', 0, new RuntimeException),
MyException::class,
'Algo va mal',
);
Assert::type(RuntimeException::class, $e->getPrevious());
Assert::error(string $callable, int|string|array $type, ?string $message=null)
Comprueba que la invocación $callable
genera los errores esperados (es decir, advertencias, avisos, etc.). Como
$type
especificamos una de las constantes E_...
, por ejemplo E_WARNING
. Y si pasamos
$message
, el mensaje de error también debe coincidir con el patrón. Por ejemplo
Assert::error(
fn() => $i++,
E_NOTICE,
'Undefined variable: i',
);
Si el callback genera más errores, debemos esperarlos todos en el orden exacto. En este caso pasamos el array en
$type
:
Assert::error(function () {
$a++;
$b++;
}, [
[E_NOTICE, 'Undefined variable: a'],
[E_NOTICE, 'Undefined variable: b'],
]);
Si $type
es el nombre de la clase, esta aserción se comporta igual que
Assert::exception()
.
Assert::noError(callable $callable)
Comprueba que la función $callable
no lanza ningún warning/notice/error o excepción de PHP. Es útil para
probar un fragmento de código en el que no hay ninguna otra aserción.
Assert::match(string $pattern, $actual, ?string $description=null)
$actual
debe coincidir con $pattern
. Podemos utilizar dos variantes de patrones: expresiones
regulares o comodines.
Si pasamos una expresión regular como $pattern
, debemos utilizar ~
or #
para
delimitarla. No se admiten otros delimitadores. Por ejemplo test donde $var
debe contener sólo dígitos
hexadecimales:
Assert::match('#^[0-9a-f]$#i', $var);
La otra variante es similar a la comparación de cadenas pero podemos utilizar algunos caracteres comodín en
$pattern
:
%a%
uno o más de cualquier cosa excepto los caracteres de fin de línea%a?%
cero o más de cualquier cosa excepto los caracteres de fin de línea%A%
uno o más de cualquier cosa incluyendo los caracteres de fin de línea%A?%
cero o más caracteres, incluidos los de final de línea%s%
uno o más caracteres de espacio en blanco excepto los caracteres de final de línea%s?%
cero o más caracteres de espacio en blanco excepto los caracteres de final de línea%S%
uno o más de caracteres excepto el espacio en blanco%S?%
cero o más de caracteres excepto el espacio en blanco%c%
un solo carácter de cualquier tipo (excepto el final de línea)%d%
uno o más dígitos%d?%
cero o más dígitos%i%
valor entero con signo%f%
número en coma flotante%h%
uno o más dígitos HEX%w%
uno o más caracteres alfanuméricos%%
un carácter %
Ejemplos:
# Again, hexadecimal number test
Assert::match('%h%', $var);
# Generalized path to file and line number
Assert::match('Error in file %a% on line %i%', $errorMessage);
Assert::matchFile(string $file, $actual, ?string $description=null)
La aserción es idéntica a Assert::match() pero el patrón se carga desde $file
.
Es útil para probar cadenas muy largas. El archivo de prueba es legible.
Assert::fail(string $message, $actual=null, $expected=null)
Esta afirmación siempre falla. Sólo es útil. Podemos pasar opcionalmente valores esperados y reales.
Expectativas
Si queremos comparar estructuras más complejas con elementos no constantes, las aserciones anteriores pueden no ser
suficientes. Por ejemplo, probamos un método que crea un nuevo usuario y devuelve sus atributos como un array. No conocemos el
valor hash de la contraseña, pero sabemos que debe ser una cadena hexadecimal. Y lo único que sabemos sobre el siguiente
elemento es que debe ser un objeto DateTime
.
En estos casos, podemos utilizar el Tester\Expect
dentro del parámetro $expected
de los métodos
Assert::equal()
y Assert::notEqual()
, que pueden utilizarse para describir fácilmente la
estructura.
use Tester\Expect;
Assert::equal([
'id' => Expect::type('int'), # we expect an integer
'username' => 'milo',
'password' => Expect::match('%h%'), # we expect a string matching pattern
'created_at' => Expect::type(DateTime::class), # we expect an instance of the class
], User::create(123, 'milo', 'RandomPaSsWoRd'));
Con Expect
, podemos hacer casi las mismas afirmaciones que con Assert
. Así que tenemos métodos como
Expect::same()
, Expect::match()
, Expect::count()
, etc. Además, podemos
encadenarlos como:
Expect::type(MyIterator::class)->andCount(5); # we expect MyIterator and items count is 5
O, podemos escribir nuestros propios manejadores de aserción.
Expect::that(function ($value) {
# return false if expectation fails
});
Investigación de aserciones fallidas
El Comprobador muestra dónde está el error cuando falla una aserción. Cuando comparamos estructuras complejas, el Probador
crea volcados de los valores comparados y los guarda en el directorio output
. Por ejemplo, cuando la prueba
imaginaria Arrays.recursive.phpt
falla, los volcados se guardan de la siguiente manera:
app/
└── tests/
├── output/
│ ├── Arrays.recursive.actual # valor real.
│ └── Arrays.recursive.expected # valor esperado
│
└── Arrays.recursive.phpt # prueba fallida
Podemos cambiar el nombre del directorio por Tester\Dumper::$dumpDir
.