Instalar pytest y tu primer test
Por qué las comprobaciones a mano dejan de funcionar, por qué un assert a secas casi no dice nada y cómo instalar pytest, escribir y ejecutar un primer archivo de test. Incluye los puntos, la línea de resumen, -q, -v y los códigos de salida.
¿Cómo quieres aprender?
El problema que vamos a resolver
Aquí tienes una función pequeña y la forma en que la mayoría de nosotros prueba una función así: ejecutarla y mirar lo que devuelve:
def discounted(price, percent):
return price - price * percent / 100
print(discounted(200, 10))
print(discounted(80, 25))
print(discounted(99, 0))180.0
60.0
99.0Lees los tres números, haces los cálculos en tu cabeza y decides que son correctos. Eso es un test. Es un test manual y tiene dos problemas.
El primero es que no escala. Tres números son fáciles de revisar a simple vista. Treinta funciones con cinco casos cada una, comprobadas de nuevo tras cada cambio, no lo son; y nadie lo hace.
El segundo es peor. Un mes después, alguien limpia la función:
def discounted(price, percent):
return price * (1 - percent // 100)$ python -c "from prices import discounted; print(discounted(200, 10))"
200Parece razonable y se ejecuta sin errores. Pero // es división entera, de modo que 10 // 100 es 0, y todos los descuentos han desaparecido silenciosamente. El código que solía funcionar y deja de hacerlo se llama una regresión, y una regresión es exactamente lo que las comprobaciones manuales pasan por alto, porque nadie vuelve a verificar los números antiguos.
La solución es dejar escritas en el código las respuestas esperadas, para que una máquina pueda comprobarlas en cada ocasión. Python ya tiene una instrucción para eso: assert. Pon las comprobaciones en un archivo llamado check_prices.py, junto a prices.py:
from prices import discounted
assert discounted(200, 10) == 180.0
assert discounted(80, 25) == 60.0
assert discounted(99, 0) == 99
print("all good")Con la función original:
$ python check_prices.py
all goodCon la versión «limpia»:
$ python check_prices.py
Traceback (most recent call last):
File "/home/you/shop/check_prices.py", line 3, in <module>
assert discounted(200, 10) == 180.0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionErrorLa regresión se detectó, lo cual ya es un avance. Pero observa lo que te dice: AssertionError, y nada más. No te indica lo que devolvió realmente discounted(200, 10). Y el script se detuvo en el primer fallo, por lo que no sabes si las otras dos comprobaciones pasan.
Este capítulo instala la herramienta que soluciona ambas cosas: pytest.
Al terminar este capítulo podrás
- Explicar por qué los tests automatizados detectan regresiones que las comprobaciones manuales pasan por alto
- Instalar pytest con
uvopip, y confirmarlo conpytest --version - Escribir un archivo de test que pytest encuentre por sí solo, y ejecutarlo
- Leer los puntos y la línea de resumen, y alternar entre
-qy-v - Decir qué significan los códigos de salida
0y1, y por qué eso importa
Prerrequisitos: antes de escribir un test. También necesitas Python 3 y saber cómo escribir una función.
Antes de escribir el test
El capítulo anterior trataba sobre decidir qué probar. Este trata sobre las cosas concretas que deben estar listas antes de poder ejecutar la primera línea de código de un test. Son cuatro, y cada una, cuando falta, produce su propio error confuso; por lo tanto, vale la pena verificarlas en orden.
1. Una carpeta de proyecto, un entorno. Todo en este capítulo vive en una sola carpeta:
shop/
├── .venv/ the project's own Python environment
├── pyproject.toml created by uv; lists pytest as a dev dependency
├── prices.py the code under test
└── test_prices.py the tests¿Por qué un entorno propio? Porque pytest tiene que estar instalado en el mismo Python que importará tu código. Si pytest vive en un Python y los paquetes de tu proyecto en otro, obtendrás errores que parecen código faltante cuando el problema real es una herramienta que falta. Un .venv por proyecto elimina las conjeturas.
2. pytest instalado en ese entorno; la siguiente sección se encarga de esto.
3. El código debe poder importarse. Un archivo de test es Python común y corriente: su primera línea será from prices import discounted. Así que, antes de escribir cualquier test, comprueba que esta importación exacta funcione desde la carpeta del proyecto:
$ python -c "from prices import discounted; print(discounted(200, 10))"
180.0Si esta línea falla, pytest fallará de la misma manera, solo que envuelto en un informe más largo. Comprobarlo por separado toma cinco segundos y separa «no se puede encontrar mi código» de «mi código es incorrecto». También significa que el nombre del módulo debe ser un nombre válido en Python: prices.py se puede importar, my-prices.py y 2prices.py no.
4. La promesa, por escrito en forma de casos. Antes de teclear un test, escribe lo que promete discounted, una fila por cada caso:
| Caso | Entrada | Esperado | | --- | --- | --- | | Descuento típico | discounted(200, 10) | 180.0 | | Un segundo descuento diferente | discounted(80, 25) | 60.0 | | Caso límite: sin descuento | discounted(99, 0) | 99 |
Y de forma igualmente deliberada, qué no está en la lista. Un porcentaje negativo o uno superior a 100: la función todavía no hace ninguna promesa sobre ellos, y un test solo congelaría lo que sea que haga hoy. (Rechazar una entrada inválida con un error es una promesa por derecho propio; capítulo cuatro). La propia aritmética de Python también: estás probando tu fórmula, no que * multiplique.
¿Por qué dos casos ordinarios y no uno? Observa la tercera fila frente a la función «limpia» del principio del capítulo: 99 * (1 - 0 // 100) sigue siendo 99. El caso del cero por ciento pasa incluso con el error presente. Un plan compuesto únicamente por casos límite puede pasar por alto justo el error que importa, y un plan compuesto por un solo caso típico pasa por alto los límites. Cada fila está ahí para detectar algo que las otras no detectarían.
El resto del capítulo convierte esta tabla en un archivo de test.
Instalar pytest
pytest no es parte de Python; es un paquete que instalas en tu proyecto. Es una herramienta para el desarrollo del código, no algo que el programa necesite cuando se ejecuta; por lo tanto, se añade como una dependencia de desarrollo.
Si tu proyecto utiliza uv, ejecuta esto en la carpeta del proyecto:
$ uv add --dev pytest
Resolved 7 packages in 59ms
Installed 5 packages in 18ms
+ iniconfig==2.3.1
+ packaging==26.3
+ pluggy==1.6.0
+ pygments==2.21.0
+ pytest==9.1.1uv lo registra en pyproject.toml bajo el grupo dev, de modo que cualquiera que configure el proyecto más adelante obtendrá la misma herramienta:
[dependency-groups]
dev = [
"pytest>=9.1.1",
]Si usas pip directamente dentro de un entorno virtual, activa el entorno y ejecuta:
$ python -m pip install pytestpip imprime el progreso de descarga y termina con:
Successfully installed iniconfig-2.3.1 packaging-26.3 pluggy-1.6.0 pygments-2.21.0 pytest-9.1.1Usar python -m pip en lugar de simplemente pip asegura que el paquete llegue al mismo Python que vas a ejecutar: una causa frecuente de «lo instalé pero no está ahí».
Ahora comprueba que haya funcionado:
$ pytest --version
pytest 9.1.1Una nota antes de continuar. Con uv, los comandos se ejecutan dentro del entorno del proyecto cuando se les añade el prefijo uv run; por ejemplo, uv run pytest --version. Con un entorno virtual activado, basta con pytest a secas. El resto de este curso escribe simplemente pytest; añade uv run delante si esa es tu forma de trabajar.
Tu primer archivo de test
Dos reglas de nomenclatura, y pytest no necesita nada más de ti:
- el nombre del archivo empieza con
test_(o termina en_test.py) - cada función que quieras ejecutar empieza con
test
Vuelve a colocar el archivo prices.py original y correcto, y a su lado crea test_prices.py:
from prices import discounted
def test_ten_percent_off():
assert discounted(200, 10) == 180.0
def test_quarter_off():
assert discounted(80, 25) == 60.0
def test_no_discount():
assert discounted(99, 0) == 99Cada comprobación de check_prices.py es ahora su propia función pequeña, y cada nombre describe qué se está comprobando. No hay ningún print, ninguna lista de tests que registrar, ningún if __name__ == "__main__". Ahora ejecuta pytest desde la carpeta del proyecto:
$ pytest
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
rootdir: /home/you/shop
collected 3 items
test_prices.py ... [100%]
============================== 3 passed in 0.01s ===============================pytest recorrió la carpeta, encontró test_prices.py gracias a su nombre, encontró las tres funciones gracias al suyo, ejecutó cada una e informó de los resultados.
Leer la salida
Analicémosla línea por línea.
platform ...: qué Python y qué pytest se ejecutaron. Vale la pena echarle un vistazo cuando los resultados difieren entre dos máquinas.rootdir: /home/you/shop: la carpeta que pytest considera la raíz del proyecto.collected 3 items: cuántos tests encontró. Encontrar tests se llama recolección. Si este número es menor de lo esperado, hay un nombre incorrecto en alguna parte.test_prices.py ...: un carácter por cada test, en el orden en que se ejecutaron. Un.significa que pasó.[100%]: progreso a lo largo de toda la ejecución.3 passed in 0.01s: la línea de resumen. Esta es la línea que debes leer primero.
Ahora vuelve a colocar la función discounted «limpia» y ejecuta de nuevo. Esta vez con -q, que veremos en detalle en un momento:
$ pytest -q
FF. [100%]
=================================== FAILURES ===================================
_____________________________ test_ten_percent_off _____________________________
def test_ten_percent_off():
> assert discounted(200, 10) == 180.0
E assert 200 == 180.0
E + where 200 = discounted(200, 10)
test_prices.py:5: AssertionError
_______________________________ test_quarter_off _______________________________
def test_quarter_off():
> assert discounted(80, 25) == 60.0
E assert 80 == 60.0
E + where 80 = discounted(80, 25)
test_prices.py:9: AssertionError
=========================== short test summary info ============================
FAILED test_prices.py::test_ten_percent_off - assert 200 == 180.0
FAILED test_prices.py::test_quarter_off - assert 80 == 60.0
2 failed, 1 passed in 0.01sLa línea de progreso es ahora FF.: una F por cada fallo. Y compara esto con el AssertionError a secas de antes:
- todos los tests se ejecutaron: el fallo en uno no detuvo a los demás, por lo que puedes ver que el cero por ciento todavía funciona
- cada fallo indica el nombre del test y la línea
assert 200 == 180.0muestra el valor que la función devolvió en realidad
Leer este informe al detalle será el tema central de todo el próximo capítulo. Por ahora, fíjate solo en que la línea de resumen cambió a 2 failed, 1 passed, y en que el mismo assert que ya conocías ahora se explica a sí mismo.
-q y -v: menos y más
La salida predeterminada es un término medio. Dos indicadores la modifican en una u otra dirección.
-q (quiet) omite el encabezado:
$ pytest -q
... [100%]
3 passed in 0.01s-v (verbose) le da a cada test su propia línea, con su nombre completo:
$ pytest -v
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
cachedir: .pytest_cache
rootdir: /home/you/shop
collecting ... collected 3 items
test_prices.py::test_ten_percent_off PASSED [ 33%]
test_prices.py::test_quarter_off PASSED [ 66%]
test_prices.py::test_no_discount PASSED [100%]
============================== 3 passed in 0.01s ===============================test_prices.py::test_ten_percent_off es el node ID del test: el archivo, dos puntos dobles, la función. Lo utilizarás más adelante para ejecutar un solo test. Aquí es también donde los nombres descriptivos de los tests dan sus frutos: en una lista con -v, test_quarter_off te dice qué pasó; test_2 no lo haría.
Usa -q cuando ejecutes tests constantemente y solo quieras el veredicto, y -v cuando quieras ver exactamente qué tests se ejecutaron.
¿Qué es .pytest_cache? Una carpeta que crea pytest para recordar cosas entre ejecuciones, como cuáles tests fallaron la última vez. Es seguro eliminarla y no debe incluirse en el control de versiones.Códigos de salida: cómo lee el resultado una máquina
Tú lees la línea de resumen. Un script, un editor o un servidor de integración no pueden: leen el código de salida, el número que cada programa devuelve a la shell al finalizar. En una shell de Unix, $? guarda el último de ellos:
$ pytest -q
... [100%]
3 passed in 0.01s
$ echo $?
0Y con la función rota, tras la ejecución que terminó en 2 failed, 1 passed:
$ echo $?
10: todo se recolectó y todo pasó1: los tests se ejecutaron y al menos uno falló
(En PowerShell la variable es $LASTEXITCODE). Esto es lo que permite que los tests se ejecuten automáticamente antes de fusionar el código: un código de salida distinto de cero detiene el proceso. Existen algunos códigos más, uno de los cuales conocerás en «Cuando algo falla» a continuación.
Dónde viven los archivos de test
En este capítulo, el archivo de test se encuentra justo al lado del código:
shop/
├── prices.py
└── test_prices.pyEso está bien para un puñado de archivos. En cambio, la mayoría de los proyectos reúnen los tests en su propia carpeta tests/. De cualquier manera, pytest los encuentra por su nombre; cómo estructurar un proyecto real y qué cambia cuando lo haces es el tema del capítulo tres.
Un ejemplo completo
Una función que convierte una puntuación en una calificación con letra, grades.py:
def letter_grade(score):
"""Turn a score from 0 to 100 into a letter."""
if score >= 80:
return "A"
if score >= 60:
return "B"
if score >= 40:
return "C"
return "F"Y sus tests, test_grades.py:
from grades import letter_grade
def test_top_score_is_an_a():
assert letter_grade(95) == "A"
def test_exactly_eighty_is_still_an_a():
assert letter_grade(80) == "A"
def test_just_below_eighty_is_a_b():
assert letter_grade(79) == "B"
def test_middle_score_is_a_c():
assert letter_grade(50) == "C"
def test_zero_is_an_f():
assert letter_grade(0) == "F"$ pytest -v
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
cachedir: .pytest_cache
rootdir: /home/you/grades
collecting ... collected 5 items
test_grades.py::test_top_score_is_an_a PASSED [ 20%]
test_grades.py::test_exactly_eighty_is_still_an_a PASSED [ 40%]
test_grades.py::test_just_below_eighty_is_a_b PASSED [ 60%]
test_grades.py::test_middle_score_is_a_c PASSED [ 80%]
test_grades.py::test_zero_is_an_f PASSED [100%]
============================== 5 passed in 0.01s ===============================Vale la pena fijarse en dos cosas.
En primer lugar, los tests en 80 y 79. Estos se sitúan a ambos lados de un límite, que es donde suelen esconderse los errores. Cambia score >= 80 por score > 80 —un descuido fácil— y ejecuta la versión silenciosa:
$ pytest -q
.F... [100%]
=================================== FAILURES ===================================
______________________ test_exactly_eighty_is_still_an_a _______________________
def test_exactly_eighty_is_still_an_a():
> assert letter_grade(80) == "A"
E AssertionError: assert 'B' == 'A'
E
E - A
E + B
test_grades.py:9: AssertionError
=========================== short test summary info ============================
FAILED test_grades.py::test_exactly_eighty_is_still_an_a - AssertionError: as...
1 failed, 4 passed in 0.01sEl segundo test, test_exactly_eighty_is_still_an_a, es el que falla: 80 ahora obtiene una 'B'. Un test únicamente en 95 nunca lo habría notado.
En segundo lugar, los nombres. En la lista de -v, cada línea se lee como una oración sobre el comportamiento de la función. Cuando uno falle dentro de unos meses, solo el nombre te dirá qué promesa se rompió.
Cuando algo falla
zsh: command not found: pytest (o bash: pytest: command not found) La shell no puede encontrar el programa pytest. O bien no está instalado, o bien está instalado en un entorno virtual que no está activo. Con uv, ejecuta uv run pytest; con pip, activa primero el entorno. python -m pytest también funciona y ejecuta pytest con la versión de Python que corresponda a python.
/usr/bin/python3: No module named pytest Ejecutaste python -m pytest y ese Python no tiene pytest. La ruta al inicio te dice qué Python era: en este caso el del sistema, no el de tu proyecto. Instala pytest en el entorno que pretendes usar.
ModuleNotFoundError: No module named 'price' durante la recolección El archivo de test no pudo importar tu código; en este caso debido a un error tipográfico, from price import ... en lugar de from prices import ...:
$ pytest -q
==================================== ERRORS ====================================
_______________________ ERROR collecting test_prices.py ________________________
ImportError while importing test module '/home/you/shop/test_prices.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.12/importlib/__init__.py:90: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
test_prices.py:1: in <module>
from price import discounted
E ModuleNotFoundError: No module named 'price'
=========================== short test summary info ============================
ERROR test_prices.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
1 error in 0.01sObserva la palabra ERROR, no FAILED: no se ejecutó ningún test porque el archivo ni siquiera se pudo cargar, y el código de salida es 2. Lee la última línea con E, y luego ejecuta la comprobación de importación de «Antes de escribir el test» (python -c "from prices import discounted") hasta que funcione por sí sola.
no tests ran in 0.01s pytest se ejecutó, pero no recolectó nada. El nombre del archivo no empieza con test_ (tests_prices.py, prices_tests.py y test.py se pasan todos por alto), o las funciones no empiezan con test (check_quarter_off se ignora silenciosamente). El código de salida aquí es 5, no 0, para que una ejecución automatizada no confunda «no se encontró nada» con «todos pasaron».
Un test pasa cuando a todas luces no debería Busca un assert faltante:
from prices import discounted
def test_quarter_off():
discounted(80, 25) == 999$ pytest -q
. [100%]
1 passed in 0.01sLa comparación se calcula y se descarta. Un test solo falla cuando algo dentro de él lanza una excepción; sin ningún assert, nada lo hace.
PytestReturnNotNoneWarning: Test functions should return None Escribiste return discounted(99, 0) == 99 en lugar de assert .... pytest ignora lo que devuelve un test, por lo que este test pasa tanto si la comparación es verdadera como si es falsa. La advertencia incluso pregunta: Did you mean to use assert instead of return?
Paso 4 de 6 — Predice
Comprueba lo que has entendido
Este archivo se guarda como test_double.py y se ejecuta pytest -q. ¿Qué caracteres aparecen al principio de la primera línea de la salida?
def double(n):
return n * 2
def test_two():
assert double(2) == 4
def test_zero():
assert double(0) == 1
def test_negative():
assert double(-3) == -6- AF
- B..F
- C.F.
- DF..
999 es a todas luces una respuesta incorrecta y, aun así, pytest reporta 1 passed. ¿Por qué?
def discounted(price, percent):
return price - price * percent / 100
def test_quarter_off():
discounted(80, 25) == 999- Apytest solo comprueba que la función se haya ejecutado, no su resultado
- BLa línea no tiene `assert`: la comparación se calcula y se descarta
- CLos números decimales no se pueden comparar con `==`
- DEl nombre de la función no empieza con `test_`
Un archivo con tests correctos se llama double_checks.py. ¿Qué ocurre al ejecutar pytest en esa carpeta?
- Apytest se detiene con un `ImportError`
- B`no tests ran`: el nombre del archivo no empieza con `test_`, por lo que nunca se busca dentro de él; código de salida `5`
- CTodos los tests se ejecutan, porque los nombres de las funciones empiezan con `test_`
- Dpytest renombra el archivo a `test_double_checks.py` por ti
Para responder necesitas una cuenta
Inicia sesión para comprobar tus respuestas
Las preguntas están arriba, y resolverlas mentalmente es lo que de verdad importa. Inicia sesión para ver las respuestas, las explicaciones y las pistas de tres niveles.
Tu turno
Crea una carpeta con un archivo temperature.py que contenga dos funciones:
c_to_f(celsius)— devuelvecelsius * 9 / 5 + 32f_to_c(fahrenheit)— devuelve(fahrenheit - 32) * 5 / 9
Luego instala pytest en el entorno de esa carpeta y confírmalo con pytest --version.
Escribe test_temperature.py con al menos cinco tests. Incluye el punto de congelación del agua (0 y 32), el punto de ebullición del agua (100 y 212), y -40, que es el mismo valor en ambas escalas. Dale a cada test un nombre que describa lo que comprueba.
Ejecútalo de tres maneras —pytest, pytest -q y pytest -v— y, después de una de ellas, verifica el código de salida.
Luego rómpelo a propósito: cambia 9 / 5 por 5 / 9 en c_to_f. Antes de ejecutar, predice cuáles tests fallarán y cómo se verá la línea de progreso. Ejecuta pytest -q, compara y comprueba de nuevo el código de salida.
Por último, renombra test_temperature.py a temperature_check.py y ejecuta pytest. Lee lo que dice y observa su código de salida. Luego vuelve a ponerle su nombre original. Saber cómo se ve «no se encontró nada» te ahorrará una tarde de confusión.
Solución
Primero el plan, antes de escribir cualquier código: la misma tabla que antes:
| Caso | Entrada | Esperado | | --- | --- | --- | | El agua se congela | c_to_f(0), f_to_c(32) | 32, 0 | | El agua hierve | c_to_f(100), f_to_c(212) | 212, 100 | | Las dos escalas coinciden | c_to_f(-40), f_to_c(-40) | -40, -40 |
temperature.py:
def c_to_f(celsius):
return celsius * 9 / 5 + 32
def f_to_c(fahrenheit):
return (fahrenheit - 32) * 5 / 9Antes de escribir los tests, la comprobación de importación desde la carpeta del proyecto:
$ python -c "import temperature; print('importable')"
importabletest_temperature.py:
from temperature import c_to_f, f_to_c
def test_water_freezes_at_32_f():
assert c_to_f(0) == 32
def test_water_boils_at_212_f():
assert c_to_f(100) == 212
def test_32_f_is_zero_c():
assert f_to_c(32) == 0
def test_212_f_is_100_c():
assert f_to_c(212) == 100
def test_minus_40_is_the_same_in_both_scales():
assert c_to_f(-40) == -40
assert f_to_c(-40) == -40$ pytest -q
..... [100%]
5 passed in 0.01s
$ echo $?
0
$ pytest -v
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
cachedir: .pytest_cache
rootdir: /home/you/temperature
collecting ... collected 5 items
test_temperature.py::test_water_freezes_at_32_f PASSED [ 20%]
test_temperature.py::test_water_boils_at_212_f PASSED [ 40%]
test_temperature.py::test_32_f_is_zero_c PASSED [ 60%]
test_temperature.py::test_212_f_is_100_c PASSED [ 80%]
test_temperature.py::test_minus_40_is_the_same_in_both_scales PASSED [100%]
============================== 5 passed in 0.01s ===============================Ahora el error intencional: 9 / 5 cambiado por 5 / 9 en c_to_f:
$ pytest -q
.F..F [100%]
=================================== FAILURES ===================================
__________________________ test_water_boils_at_212_f ___________________________
def test_water_boils_at_212_f():
> assert c_to_f(100) == 212
E assert 87.55555555555556 == 212
E + where 87.55555555555556 = c_to_f(100)
test_temperature.py:9: AssertionError
___________________ test_minus_40_is_the_same_in_both_scales ___________________
def test_minus_40_is_the_same_in_both_scales():
> assert c_to_f(-40) == -40
E assert 9.777777777777779 == -40
E + where 9.777777777777779 = c_to_f(-40)
test_temperature.py:21: AssertionError
=========================== short test summary info ============================
FAILED test_temperature.py::test_water_boils_at_212_f - assert 87.55555555555...
FAILED test_temperature.py::test_minus_40_is_the_same_in_both_scales - assert...
2 failed, 3 passed in 0.01s
$ echo $?
1Y el archivo renombrado:
$ pytest -q
no tests ran in 0.01s
$ echo $?
5Por qué está escrito de esta manera:
- Un comportamiento por test, nombrado como una oración. En la lista de
-v, cada línea indica lo prometido. Cuandotest_water_boils_at_212_ffalla, sabes qué se rompió antes de seguir leyendo. - Valores conocidos y comprobables.
0/32,100/212y-40son hechos que puedes verificar sin una calculadora, por lo que un fallo apunta al código y no a un valor esperado dudoso. - Ambas direcciones.
c_to_fyf_to_cson piezas de código independientes, por lo que cada una necesita sus propios tests; que una esté bien no dice nada sobre la otra. - La ejecución con el error es la parte más reveladora. Solo fallaron dos tests, y
test_water_freezes_at_32_fno fue uno de ellos:0 * 5 / 9 + 32sigue siendo32, porque multiplicar por cero oculta cualquier multiplicador. Esa es exactamente la razón por la que el plan tiene más de un caso: una suite que solo tuviera el punto de congelación habría pasado con el error presente. El test de-40tiene dos líneas conassert; la primera falló, por lo que la segunda nunca llegó a ejecutarse. Eso es aceptable aquí porque ambas describen la misma promesa. - Los códigos de salida —
0,1,5— son los tres con los que te toparás con más frecuencia: todos pasaron, algo falló, no se encontró nada.
Paso 6 de 6
Reto — el quiz del capítulo
Diez preguntas de fácil a difícil. Las últimas son difíciles a propósito.
Inicia sesión para hacer el quiz