Nette Documentation Preview

syntax
Aserciones
**********

.[perex]
Las aserciones sirven para confirmar que el valor real coincide con el esperado. Son métodos de la clase `Tester\Assert`.

Elija las aserciones más adecuadas. `Assert::same($a, $b)` es mejor que `Assert::true($a === $b)`, porque en caso de fallo muestra un mensaje de error con sentido. En el segundo caso solo obtenemos `false should be true`, que no nos dice nada sobre el contenido de las variables `$a` y `$b`.

La mayoría de las aserciones pueden llevar además una descripción opcional en el parámetro `$description`, que se muestra en el mensaje de error si la expectativa falla.

Los ejemplos dan por hecho que se ha creado el siguiente alias:

```php
use Tester\Assert;
```


Assert::same($expected, $actual, ?string $description=null) .[method]
---------------------------------------------------------------------
`$expected` debe ser idéntico a `$actual`. Es lo mismo que el operador `===` de PHP.


Assert::notSame($expected, $actual, ?string $description=null) .[method]
------------------------------------------------------------------------
Lo contrario de `Assert::same()`, es decir, es lo mismo que el operador `!==` de PHP.


Assert::equal($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false) .[method]
-------------------------------------------------------------------------------------------------------------------------
`$expected` debe ser igual a `$actual`. A diferencia de `Assert::same()`, se ignoran la identidad de los objetos, el orden de los pares clave => valor de los arrays y las diferencias mínimas entre números decimales, lo que se puede cambiar con `$matchIdentity` y `$matchOrder`.

Los siguientes casos son iguales desde la perspectiva de `equal()`, pero no de `same()`:

```php
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
	['first' => 11, 'second' => 22],
	['second' => 22, 'first' => 11],
);
```

Pero cuidado, los arrays `[1, 2]` y `[2, 1]` no son iguales, porque solo difiere el orden de los valores, no los pares clave => valor. El array `[1, 2]` también se puede escribir como `[0 => 1, 1 => 2]`, y `[1 => 2, 0 => 1]` se considerará por tanto igual.

En `$expected` también puede usar las llamadas [#Expectativas].


Assert::notEqual($expected, $actual, ?string $description=null) .[method]
-------------------------------------------------------------------------
Lo contrario de `Assert::equal()`.


Assert::contains($needle, string|array $actual, ?string $description=null) .[method]
------------------------------------------------------------------------------------
Si `$actual` es una cadena, debe contener la subcadena `$needle`. Si es un array, debe contener el elemento `$needle` (comparado de forma estricta).


Assert::notContains($needle, string|array $actual, ?string $description=null) .[method]
---------------------------------------------------------------------------------------
Lo contrario de `Assert::contains()`.


Assert::hasKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4.0}
----------------------------------------------------------------------------------------------------------
`$actual` debe ser un array y debe contener la clave `$needle`.


Assert::hasNotKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4.0}
-------------------------------------------------------------------------------------------------------------
`$actual` debe ser un array y no debe contener la clave `$needle`.


Assert::true($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` debe ser `true`, es decir, `$value === true`.


Assert::truthy($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` debe ser truthy, es decir, cumplir la condición `if ($value) ...`.


Assert::false($value, ?string $description=null) .[method]
----------------------------------------------------------
`$value` debe ser `false`, es decir, `$value === false`.


Assert::falsey($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` debe ser falsey, es decir, cumplir la condición `if (!$value) ...`.


Assert::null($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` debe ser `null`, es decir, `$value === null`.


Assert::notNull($value, ?string $description=null) .[method]
------------------------------------------------------------
`$value` no debe ser `null`, es decir, `$value !== null`.


Assert::nan($value, ?string $description=null) .[method]
--------------------------------------------------------
`$value` debe ser Not a Number. Para probar valores NAN use exclusivamente `Assert::nan()`. El valor NAN es muy particular y aserciones como `Assert::same()` o `Assert::equal()` pueden comportarse de forma inesperada.


Assert::count($count, Countable|array $value, ?string $description=null) .[method]
----------------------------------------------------------------------------------
El número de elementos de `$value` debe ser `$count`. Es lo mismo que `count($value) === $count`.


Assert::type(string|object $type, $value, ?string $description=null) .[method]
------------------------------------------------------------------------------
`$value` debe ser del tipo dado. Como `$type` podemos usar una cadena:
- `array`
- `list`: array indexado según una serie ascendente de claves numéricas desde cero
- `bool`
- `callable`
- `float`
- `int`
- `null`
- `object`
- `resource`
- `scalar`
- `string`
- el nombre de una clase, o directamente un objeto, y entonces debe cumplirse `$value instanceof $type`


Assert::exception(callable $callable, string $class, ?string $message=null, $code=null) .[method]
-------------------------------------------------------------------------------------------------
Al llamar a `$callable` debe lanzarse una excepción de la clase `$class`. Si indicamos `$message`, el mensaje de la excepción debe además [encajar con el patrón |#Assert::match()]. Y si indicamos `$code`, los códigos deben coincidir además de forma estricta.

Por ejemplo, la siguiente prueba fallará porque el mensaje de la excepción no coincide:

```php
Assert::exception(
	fn() => throw new App\InvalidValueException('Zero value'),
	App\InvalidValueException::class,
	'Value is too low',
);
```

`Assert::exception()` devuelve la excepción lanzada, lo que le permite probar también una excepción anidada.

```php
$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) .[method]
----------------------------------------------------------------------------------------
Comprueba que la función `$callable` generó los errores esperados (es decir, warnings, notices, etc.). Como `$type` indique una de las constantes `E_...`, por ejemplo `E_WARNING`. Y si indicamos `$message`, el mensaje de error debe además [encajar con el patrón |#Assert::match()]. Por ejemplo:

```php
Assert::error(
	fn() => $i++,
	E_NOTICE,
	'Undefined variable: i',
);
```

Si el callback genera más errores, debemos esperarlos todos en el orden exacto. En ese caso, pase un array en `$type`:

```php
Assert::error(function () {
	$a++;
	$b++;
}, [
	[E_NOTICE, 'Undefined variable: a'],
	[E_NOTICE, 'Undefined variable: b'],
]);
```

.[note]
Si indica como `$type` el nombre de una clase, se comporta igual que `Assert::exception()`.


Assert::noError(callable $callable) .[method]
---------------------------------------------
Comprueba que la función `$callable` no generó ningún warning, error ni excepción. Es útil para probar fragmentos de código en los que no hay ninguna otra aserción.


Assert::match(string $pattern, $actual, ?string $description=null) .[method]
----------------------------------------------------------------------------
`$actual` debe encajar con el patrón `$pattern`. Podemos usar dos variantes de patrones: expresiones regulares o comodines.

Si pasamos como `$pattern` una expresión regular, debemos delimitarla con `~` o `#`. Otros delimitadores no están soportados. Por ejemplo, una prueba en la que `$var` solo puede contener dígitos hexadecimales:

```php
Assert::match('#^[0-9a-f]+$#i', $var);
```

La segunda variante es parecida a comparar cadenas corrientes, pero en `$pattern` podemos usar varios comodines:

- `%a%` uno o más caracteres cualesquiera salvo los de fin de línea
- `%a?%` cero o más caracteres cualesquiera salvo los de fin de línea
- `%A%` uno o más caracteres cualesquiera, incluidos los de fin de línea
- `%A?%` cero o más caracteres cualesquiera, incluidos los de fin de línea
- `%s%` uno o más caracteres de espacio en blanco salvo los de fin de línea
- `%s?%` cero o más caracteres de espacio en blanco salvo los de fin de línea
- `%S%` uno o más caracteres que no sean espacio en blanco
- `%S?%` cero o más caracteres que no sean espacio en blanco
- `%c%` un único carácter de cualquier tipo (salvo el fin 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 hexadecimales
- `%w%` uno o más caracteres alfanuméricos
- `%ds%` separador de directorios (`/` o `\`)
- `%%` un carácter %

Ejemplos:

```php
# De nuevo, prueba de un número hexadecimal
Assert::match('%h%', $var);

# Generalización de la ruta del archivo y del número de línea
Assert::match('Error in file %a% on line %i%', $errorMessage);
```


Assert::notMatch(string $pattern, $actual, ?string $description=null) .[method]{data-version:2.5.6}
---------------------------------------------------------------------------------------------------
Lo contrario de `Assert::match()`.


Assert::matchFile(string $file, $actual, ?string $description=null) .[method]
-----------------------------------------------------------------------------
Esta aserción es idéntica a [#Assert::match()], pero el patrón se carga del archivo `$file`. Es útil para probar cadenas muy largas. El archivo de prueba se mantiene claro.


Assert::fail(string $message, $actual=null, $expected=null) .[method]
---------------------------------------------------------------------
Esta aserción falla siempre. A veces simplemente viene bien. Opcionalmente podemos indicar el valor esperado y el real.


Expectativas
------------
Cuando queremos comparar estructuras más complejas con elementos no constantes, las aserciones mencionadas arriba pueden no bastar. Por ejemplo, estamos probando un método que crea un usuario nuevo y devuelve sus atributos como array. No sabemos el valor del hash de la contraseña, pero sí que debe ser una cadena hexadecimal. Y del siguiente elemento solo sabemos que debe ser un objeto `DateTime`.

En esas situaciones podemos usar `Tester\Expect` dentro del parámetro `$expected` de los métodos `Assert::equal()` y `Assert::notEqual()`, con el que la estructura se puede describir con facilidad.

```php
use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # esperamos un entero
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # esperamos una cadena que encaje con el patrón
	'created_at' => Expect::type(DateTime::class), # esperamos una instancia de la clase
], User::create(123, 'milo', 'RandomPaSsWoRd'));
```

Con `Expect` podemos hacer casi las mismas aserciones que con `Assert`. Así, tenemos a disposición los métodos `Expect::same()`, `Expect::match()`, `Expect::count()`, etc. Además, los podemos encadenar:

```php
Expect::type(MyIterator::class)->andCount(5);  # esperamos MyIterator y que el número de elementos sea 5
```

Alternativamente podemos escribir nuestros propios manejadores de aserciones.

```php
Expect::that(function ($value) {
	# devuelve false si la expectativa falla
});
```


Explorar las aserciones fallidas
--------------------------------
Cuando una aserción falla, Tester imprime en qué consiste el error. Si comparamos estructuras complejas, Tester crea volcados de los valores comparados y los guarda en el directorio `output`. Por ejemplo, si falla la prueba ficticia `Arrays.recursive.phpt`, los volcados se guardarán así:

```
app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # valor real
	│   └── Arrays.recursive.expected  # valor esperado
	│
	└── Arrays.recursive.phpt          # prueba que falla
```

Podemos cambiar el nombre del directorio con `Tester\Dumper::$dumpDir`.

Aserciones

Las aserciones sirven para confirmar que el valor real coincide con el esperado. Son métodos de la clase Tester\Assert.

Elija las aserciones más adecuadas. Assert::same($a, $b) es mejor que Assert::true($a === $b), porque en caso de fallo muestra un mensaje de error con sentido. En el segundo caso solo obtenemos false should be true, que no nos dice nada sobre el contenido de las variables $a y $b.

La mayoría de las aserciones pueden llevar además una descripción opcional en el parámetro $description, que se muestra en el mensaje de error si la expectativa falla.

Los ejemplos dan por hecho que se ha creado el siguiente alias:

use Tester\Assert;

Assert::same($expected, $actual, ?string $description=null)

$expected debe ser idéntico a $actual. Es lo mismo que el operador === de PHP.

Assert::notSame($expected, $actual, ?string $description=null)

Lo contrario de Assert::same(), es decir, es lo mismo que el operador !== de 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 de los objetos, el orden de los pares clave ⇒ valor de los arrays y las diferencias mínimas entre números decimales, lo que se puede cambiar con $matchIdentity y $matchOrder.

Los siguientes casos son iguales desde la perspectiva de equal(), pero no 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],
);

Pero cuidado, los arrays [1, 2] y [2, 1] no son iguales, porque solo difiere el orden de los valores, no los pares clave ⇒ valor. El array [1, 2] también se puede escribir como [0 => 1, 1 => 2], y [1 => 2, 0 => 1] se considerará por tanto igual.

En $expected también puede usar las llamadas Expectativas.

Assert::notEqual($expected, $actual, ?string $description=null)

Lo contrario de Assert::equal().

Assert::contains($needle, string|array $actual, ?string $description=null)

Si $actual es una cadena, debe contener la subcadena $needle. Si es un array, debe contener el elemento $needle (comparado de forma estricta).

Assert::notContains($needle, string|array $actual, ?string $description=null)

Lo contrario de Assert::contains().

Assert::hasKey(string|int $needle, array $actual, ?string $description=null)

$actual debe ser un array y debe contener la clave $needle.

Assert::hasNotKey(string|int $needle, array $actual, ?string $description=null)

$actual debe ser un array y no debe contener la clave $needle.

Assert::true($value, ?string $description=null)

$value debe ser true, es decir, $value === true.

Assert::truthy($value, ?string $description=null)

$value debe ser truthy, es decir, cumplir la condición if ($value) ....

Assert::false($value, ?string $description=null)

$value debe ser false, es decir, $value === false.

Assert::falsey($value, ?string $description=null)

$value debe ser falsey, es decir, cumplir la condición if (!$value) ....

Assert::null($value, ?string $description=null)

$value debe ser null, es decir, $value === null.

Assert::notNull($value, ?string $description=null)

$value no debe ser null, es decir, $value !== null.

Assert::nan($value, ?string $description=null)

$value debe ser Not a Number. Para probar valores NAN use exclusivamente Assert::nan(). El valor NAN es muy particular y aserciones como Assert::same() o Assert::equal() pueden comportarse de forma inesperada.

Assert::count($count, Countable|array $value, ?string $description=null)

El número de elementos de $value debe ser $count. Es lo mismo que count($value) === $count.

Assert::type(string|object $type, $value, ?string $description=null)

$value debe ser del tipo dado. Como $type podemos usar una cadena:

  • array
  • list: array indexado según una serie ascendente de claves numéricas desde cero
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • el nombre de una clase, o directamente un objeto, y entonces debe cumplirse $value instanceof $type

Assert::exception(callable $callable, string $class, ?string $message=null, $code=null)

Al llamar a $callable debe lanzarse una excepción de la clase $class. Si indicamos $message, el mensaje de la excepción debe además encajar con el patrón. Y si indicamos $code, los códigos deben coincidir además de forma estricta.

Por ejemplo, la siguiente prueba fallará porque el mensaje de la excepción no coincide:

Assert::exception(
	fn() => throw new App\InvalidValueException('Zero value'),
	App\InvalidValueException::class,
	'Value is too low',
);

Assert::exception() devuelve la excepción lanzada, lo que le permite probar también una excepción anidada.

$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)

Comprueba que la función $callable generó los errores esperados (es decir, warnings, notices, etc.). Como $type indique una de las constantes E_..., por ejemplo E_WARNING. Y si indicamos $message, el mensaje de error debe además encajar 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 ese caso, pase un array en $type:

Assert::error(function () {
	$a++;
	$b++;
}, [
	[E_NOTICE, 'Undefined variable: a'],
	[E_NOTICE, 'Undefined variable: b'],
]);

Si indica como $type el nombre de una clase, se comporta igual que Assert::exception().

Assert::noError(callable $callable)

Comprueba que la función $callable no generó ningún warning, error ni excepción. Es útil para probar fragmentos de código en los que no hay ninguna otra aserción.

Assert::match(string $pattern, $actual, ?string $description=null)

$actual debe encajar con el patrón $pattern. Podemos usar dos variantes de patrones: expresiones regulares o comodines.

Si pasamos como $pattern una expresión regular, debemos delimitarla con ~ o #. Otros delimitadores no están soportados. Por ejemplo, una prueba en la que $var solo puede contener dígitos hexadecimales:

Assert::match('#^[0-9a-f]+$#i', $var);

La segunda variante es parecida a comparar cadenas corrientes, pero en $pattern podemos usar varios comodines:

  • %a% uno o más caracteres cualesquiera salvo los de fin de línea
  • %a?% cero o más caracteres cualesquiera salvo los de fin de línea
  • %A% uno o más caracteres cualesquiera, incluidos los de fin de línea
  • %A?% cero o más caracteres cualesquiera, incluidos los de fin de línea
  • %s% uno o más caracteres de espacio en blanco salvo los de fin de línea
  • %s?% cero o más caracteres de espacio en blanco salvo los de fin de línea
  • %S% uno o más caracteres que no sean espacio en blanco
  • %S?% cero o más caracteres que no sean espacio en blanco
  • %c% un único carácter de cualquier tipo (salvo el fin 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 hexadecimales
  • %w% uno o más caracteres alfanuméricos
  • %ds% separador de directorios (/ o \)
  • %% un carácter %

Ejemplos:

# De nuevo, prueba de un número hexadecimal
Assert::match('%h%', $var);

# Generalización de la ruta del archivo y del número de línea
Assert::match('Error in file %a% on line %i%', $errorMessage);

Assert::notMatch(string $pattern, $actual, ?string $description=null)

Lo contrario de Assert::match().

Assert::matchFile(string $file, $actual, ?string $description=null)

Esta aserción es idéntica a Assert::match(), pero el patrón se carga del archivo $file. Es útil para probar cadenas muy largas. El archivo de prueba se mantiene claro.

Assert::fail(string $message, $actual=null, $expected=null)

Esta aserción falla siempre. A veces simplemente viene bien. Opcionalmente podemos indicar el valor esperado y el real.

Expectativas

Cuando queremos comparar estructuras más complejas con elementos no constantes, las aserciones mencionadas arriba pueden no bastar. Por ejemplo, estamos probando un método que crea un usuario nuevo y devuelve sus atributos como array. No sabemos el valor del hash de la contraseña, pero sí que debe ser una cadena hexadecimal. Y del siguiente elemento solo sabemos que debe ser un objeto DateTime.

En esas situaciones podemos usar Tester\Expect dentro del parámetro $expected de los métodos Assert::equal() y Assert::notEqual(), con el que la estructura se puede describir con facilidad.

use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # esperamos un entero
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # esperamos una cadena que encaje con el patrón
	'created_at' => Expect::type(DateTime::class), # esperamos una instancia de la clase
], User::create(123, 'milo', 'RandomPaSsWoRd'));

Con Expect podemos hacer casi las mismas aserciones que con Assert. Así, tenemos a disposición los métodos Expect::same(), Expect::match(), Expect::count(), etc. Además, los podemos encadenar:

Expect::type(MyIterator::class)->andCount(5);  # esperamos MyIterator y que el número de elementos sea 5

Alternativamente podemos escribir nuestros propios manejadores de aserciones.

Expect::that(function ($value) {
	# devuelve false si la expectativa falla
});

Explorar las aserciones fallidas

Cuando una aserción falla, Tester imprime en qué consiste el error. Si comparamos estructuras complejas, Tester crea volcados de los valores comparados y los guarda en el directorio output. Por ejemplo, si falla la prueba ficticia Arrays.recursive.phpt, los volcados se guardarán así:

app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # valor real
	│   └── Arrays.recursive.expected  # valor esperado
	│
	└── Arrays.recursive.phpt          # prueba que falla

Podemos cambiar el nombre del directorio con Tester\Dumper::$dumpDir.