python testing pytest assert

¿Es posible cambiar el comportamiento de la declaración de afirmación de PyTest en Python?



testing assert (4)

Estoy usando declaraciones de afirmación de Python para que coincida con el comportamiento real y esperado. No tengo control sobre estos, ya que si hay una prueba de error, aborta los casos. Quiero tomar el control del error de afirmación y quiero definir si quiero abortar el caso de prueba en caso de fallo o no.

También quiero agregar algo como si hay un error de aserción, entonces el caso de prueba debe pausarse y el usuario puede reanudar en cualquier momento.

No tengo idea de cómo hacer esto

Ejemplo de código, estamos usando pytest aquí

import pytest def test_abc(): a = 10 assert a == 10, "some error message"

Below is my expectation

Cuando afirmar arroja un aserción de error, debería tener la opción de pausar el caso de prueba y puedo depurar y luego reanudar. Para pausar y reanudar, tkinter módulo tkinter . Haré una función de aserción como a continuación

import tkinter import tkinter.messagebox top = tkinter.Tk() def _assertCustom(assert_statement, pause_on_fail = 0): #assert_statement will be something like: assert a == 10, "Some error" #pause_on_fail will be derived from global file where I can change it on runtime if pause_on_fail == 1: try: eval(assert_statement) except AssertionError as e: tkinter.messagebox.showinfo(e) eval (assert_statement) #Above is to raise the assertion error again to fail the testcase else: eval (assert_statement)

En el futuro, tengo que cambiar cada declaración de aserción con esta función como

import pytest def test_abc(): a = 10 # Suppose some code and below is the assert statement _assertCustom("assert a == 10, ''error message''")

Este es un esfuerzo demasiado grande para mí, ya que tengo que hacer cambios en miles de lugares donde he usado la afirmación. ¿Hay alguna manera fácil de hacerlo en pytest

Summary: necesito algo donde pueda pausar el caso de prueba en caso de falla y luego reanudarlo después de la depuración. Sé sobre tkinter y esa es la razón por la que lo he usado. Cualquier otra idea será bienvenida

Note : el código anterior aún no se ha probado. También puede haber pequeños errores de sintaxis

Editar: Gracias por las respuestas. Extendiendo esta pregunta un poco más adelante ahora. ¿Qué pasa si quiero cambiar el comportamiento de afirmar? Actualmente, cuando hay un error de aserción, sale el caso de prueba. ¿Qué pasa si quiero elegir si necesito salir de caso de prueba en caso de fallo de afirmación particular o no? No quiero escribir una función de aserción personalizada como se mencionó anteriormente porque de esta manera tengo que cambiar en varios lugares


Está utilizando pytest , que le brinda amplias opciones para interactuar con las pruebas pytest . Le ofrece opciones de línea de comandos y varios ganchos para que esto sea posible. Explicaré cómo usar cada uno y dónde puede hacer personalizaciones para satisfacer sus necesidades específicas de depuración.

También entraré en opciones más exóticas que le permitirán omitir afirmaciones específicas por completo, si realmente siente que debe hacerlo.

Manejar excepciones, no afirmar

Tenga en cuenta que una prueba fallida normalmente no detiene pytest; solo si habilitó explícitamente decirle que salga después de un cierto número de fallas . Además, las pruebas fallan porque se genera una excepción; AssertionError plantea AssertionError pero esa no es la única excepción que hará que una prueba falle. Desea controlar cómo se manejan las excepciones, no alterar la assert .

Sin embargo, una afirmación fallida finalizará la prueba individual. Eso es porque una vez que se genera una excepción fuera de un try...except bloque, Python desenrolla el marco de la función actual, y no hay retroceso en eso.

No creo que eso sea lo que desea, a juzgar por su descripción de sus intentos _assertCustom() de volver a ejecutar la afirmación, pero no obstante, analizaré sus opciones más adelante.

Depuración post-mortem en pytest con pdb

Para las diversas opciones para manejar fallas en un depurador, comenzaré con el --pdb de línea de comandos --pdb , que abre el indicador de depuración estándar cuando falla una prueba (la salida se elije por brevedad):

$ mkdir demo $ touch demo/__init__.py $ cat << EOF > demo/test_foo.py > def test_ham(): > assert 42 == 17 > def test_spam(): > int("Vikings") > EOF $ pytest demo/test_foo.py --pdb [ ... ] test_foo.py:2: AssertionError >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > /.../demo/test_foo.py(2)test_ham() -> assert 42 == 17 (Pdb) q Exit: Quitting debugger [ ... ]

Con este modificador, cuando una prueba falla, pytest inicia una sesión de depuración post mortem . Esto es esencialmente exactamente lo que querías; para detener el código en el punto de una prueba fallida y abrir el depurador para ver el estado de su prueba. Puede interactuar con las variables locales de la prueba, los globales y los locales y globales de cada cuadro en la pila.

Aquí pytest le da control total sobre si salir o no después de este punto: si usa el comando q quit, entonces pytest también sale de la ejecución, usando c para continuar devolverá el control a pytest y se ejecutará la siguiente prueba.

Usar un depurador alternativo

No está obligado al depurador pdb para esto; puede configurar un depurador diferente con el --pdbcls . Cualquier implementación compatible con pdb.Pdb() funcionaría, incluida la implementación del depurador de IPython , o la mayoría de los otros depuradores de Python (el depurador de pudb requiere que se use el modificador -s , o un complemento especial ). El conmutador toma un módulo y una clase, por ejemplo, para usar pudb podría usar:

$ pytest -s --pdb --pdbcls=pudb.debugger:Debugger

Puede usar esta función para escribir su propia clase de envoltura alrededor de Pdb que simplemente regrese de inmediato si la falla específica no es algo que le interese. pytest usa Pdb() exactamente como pdb.post_mortem() :

p = Pdb() p.reset() p.interaction(None, t)

Aquí, t es un objeto de rastreo . Cuando p.interaction(None, t) regresa, pytest continúa con la siguiente prueba, a menos que p.quitting esté establecido en True (en ese momento, pytest sale).

Aquí hay un ejemplo de implementación que muestra que declinamos depurar y regresa de inmediato, a menos que la prueba ValueError , guardado como demo/custom_pdb.py :

import pdb, sys class CustomPdb(pdb.Pdb): def interaction(self, frame, traceback): if sys.last_type is not None and not issubclass(sys.last_type, ValueError): print("Sorry, not interested in this failure") return return super().interaction(frame, traceback)

Cuando uso esto con la demostración anterior, esta es la salida (nuevamente, elegida por brevedad):

$ pytest test_foo.py -s --pdb --pdbcls=demo.custom_pdb:CustomPdb [ ... ] def test_ham(): > assert 42 == 17 E assert 42 == 17 test_foo.py:2: AssertionError >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Sorry, not interested in this failure F >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> def test_spam(): > int("Vikings") E ValueError: invalid literal for int() with base 10: ''Vikings'' test_foo.py:4: ValueError >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > /.../test_foo.py(4)test_spam() -> int("Vikings") (Pdb)

Las introspectivas anteriores sys.last_type para determinar si la falla es ''interesante''.

Sin embargo, realmente no puedo recomendar esta opción a menos que desee escribir su propio depurador usando tkInter o algo similar. Tenga en cuenta que es una gran empresa.

Fallas de filtrado; elegir y elegir cuándo abrir el depurador

El siguiente nivel es la depuración de pytest y los ganchos de interacción ; estos son puntos de enlace para personalizaciones de comportamiento, para reemplazar o mejorar la forma en que pytest normalmente maneja cosas como manejar una excepción o ingresar el depurador a través de pdb.set_trace() o breakpoint() (Python 3.7 o más reciente).

La implementación interna de este enlace también es responsable de imprimir el banner >>> entering PDB >>> anterior, por lo que usar este enlace para evitar que se ejecute el depurador significa que no verá esta salida en absoluto. Puede tener su propio gancho y luego delegarlo al gancho original cuando un fallo de prueba es ''interesante'', y así filtrar los fallos de prueba independientemente del depurador que esté utilizando. Puede acceder a la implementación interna accediendo por nombre ; El complemento de pdbinvoke interno para esto se llama pdbinvoke . Para evitar que se ejecute, debe anular el registro pero guardar una referencia, ¿podemos llamarlo directamente según sea necesario?

Aquí hay una implementación de muestra de tal gancho; puede poner esto en cualquiera de las ubicaciones desde las que se cargan los complementos ; Lo puse en demo/conftest.py :

import pytest @pytest.hookimpl(trylast=True) def pytest_configure(config): # unregister returns the unregistered plugin pdbinvoke = config.pluginmanager.unregister(name="pdbinvoke") if pdbinvoke is None: # no --pdb switch used, no debugging requested return # get the terminalreporter too, to write to the console tr = config.pluginmanager.getplugin("terminalreporter") # create or own plugin plugin = ExceptionFilter(pdbinvoke, tr) # register our plugin, pytest will then start calling our plugin hooks config.pluginmanager.register(plugin, "exception_filter") class ExceptionFilter: def __init__(self, pdbinvoke, terminalreporter): # provide the same functionality as pdbinvoke self.pytest_internalerror = pdbinvoke.pytest_internalerror self.orig_exception_interact = pdbinvoke.pytest_exception_interact self.tr = terminalreporter def pytest_exception_interact(self, node, call, report): if not call.excinfo. errisinstance(ValueError): self.tr.write_line("Sorry, not interested!") return return self.orig_exception_interact(node, call, report)

El complemento anterior usa el complemento interno TerminalReporter para escribir líneas en el terminal; esto hace que la salida sea más limpia cuando se usa el formato de estado de prueba compacto predeterminado y le permite escribir cosas en el terminal incluso con la captura de salida habilitada.

El ejemplo registra el objeto del complemento con el pytest_exception_interact través de otro pytest_configure() , pytest_configure() , pero asegurándose de que se ejecute lo suficientemente tarde (usando @pytest.hookimpl(trylast=True) ) para poder pdbinvoke el registro del complemento interno pdbinvoke . Cuando se llama al gancho, el ejemplo prueba contra el objeto call.exceptinfo ; También puede comprobar el node o el report también.

Con el código de ejemplo anterior en demo/conftest.py , se ignora el test_spam prueba test_spam , solo el test_spam prueba test_spam , que aumenta ValueError , da como resultado la apertura de la solicitud de depuración:

$ pytest demo/test_foo.py --pdb [ ... ] demo/test_foo.py F Sorry, not interested! demo/test_foo.py F >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> def test_spam(): > int("Vikings") E ValueError: invalid literal for int() with base 10: ''Vikings'' demo/test_foo.py:4: ValueError >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > /.../demo/test_foo.py(4)test_spam() -> int("Vikings") (Pdb)

Para repetir, el enfoque anterior tiene la ventaja adicional de que puede combinar esto con cualquier depurador que funcione con pytest , incluido pudb, o el depurador de IPython:

$ pytest demo/test_foo.py --pdb --pdbcls=IPython.core.debugger:Pdb [ ... ] demo/test_foo.py F Sorry, not interested! demo/test_foo.py F >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> def test_spam(): > int("Vikings") E ValueError: invalid literal for int() with base 10: ''Vikings'' demo/test_foo.py:4: ValueError >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > /.../demo/test_foo.py(4)test_spam() 1 def test_ham(): 2 assert 42 == 17 3 def test_spam(): ----> 4 int("Vikings") ipdb>

También tiene mucho más contexto sobre qué prueba se estaba ejecutando (a través del argumento del node ) y acceso directo a la excepción call.excinfo (a través de la instancia call.excinfo ExceptionInfo ).

Tenga en cuenta que los complementos específicos del depurador de pytest (como pytest-pudb o pytest-pycharm ) registran su propio pytest_exception_interact hooksp. Una implementación más completa tendría que recorrer todos los complementos en el administrador de complementos para anular complementos arbitrarios, automáticamente, usando config.pluginmanager.list_name_plugin y hasattr() para probar cada complemento.

Hacer que los fracasos desaparezcan por completo

Si bien esto le da un control total sobre la depuración de prueba fallida, esto todavía deja la prueba como fallida, incluso si optó por no abrir el depurador para una prueba determinada. Si desea que las fallas desaparezcan por completo, puede usar un gancho diferente: pytest_runtest_call() .

Cuando pytest ejecuta pruebas, ejecutará la prueba a través del enlace anterior, que se espera que devuelva None o genere una excepción. A partir de esto, se crea un informe, opcionalmente se crea una entrada de registro, y si la prueba falla, se llama al pytest_exception_interact() mencionado pytest_exception_interact() . Entonces, todo lo que necesita hacer es cambiar el resultado que produce este gancho; en lugar de una excepción, simplemente no debería devolver nada en absoluto.

La mejor manera de hacerlo es usar una envoltura de gancho . Los envoltorios de ganchos no tienen que hacer el trabajo real, sino que tienen la oportunidad de alterar lo que sucede con el resultado de un gancho. Todo lo que tienes que hacer es agregar la línea:

outcome = yield

en su implementación de envoltorio de gancho y obtiene acceso al resultado del gancho , incluida la excepción de prueba a través de outcome.excinfo . Este atributo se establece en una tupla de (tipo, instancia, rastreo) si se produjo una excepción en la prueba. Alternativamente, puede llamar a outcome.get_result() y usar el try...except estándar try...except manejo.

Entonces, ¿cómo se pasa una prueba fallida? Tienes 3 opciones básicas:

  • Puede marcar la prueba como una falla esperada , llamando a pytest.xfail() en el contenedor.
  • Puede marcar el elemento como omitido , lo que pretende que la prueba nunca se ejecutó en primer lugar, llamando a pytest.skip() .
  • Puede eliminar la excepción, utilizando el método outcome.force_result() ; establezca el resultado en una lista vacía aquí (es decir: el enlace registrado no produjo nada más que None ), y la excepción se borra por completo.

Lo que uses depende de ti. Asegúrese de verificar primero el resultado de las pruebas omitidas y de falla esperada, ya que no necesita manejar esos casos como si la prueba fallara. Puede acceder a las excepciones especiales que pytest.skip.Exception estas opciones a través de pytest.skip.Exception y pytest.xfail.Exception .

Aquí hay un ejemplo de implementación que marca las pruebas fallidas que no ValueError , como omitido :

import pytest @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item): outcome = yield try: outcome.get_result() except (pytest.xfail.Exception, pytest.skip.Exception, pytest.exit.Exception): raise # already xfailed, skipped or explicit exit except ValueError: raise # not ignoring except (pytest.fail.Exception, Exception): # turn everything else into a skip pytest.skip("[NOTRUN] ignoring everything but ValueError")

Cuando se pone en conftest.py la salida se convierte en:

$ pytest -r a demo/test_foo.py ============================= test session starts ============================= platform darwin -- Python 3.8.0, pytest-3.10.0, py-1.7.0, pluggy-0.8.0 rootdir: ..., inifile: collected 2 items demo/test_foo.py sF [100%] =================================== FAILURES =================================== __________________________________ test_spam ___________________________________ def test_spam(): > int("Vikings") E ValueError: invalid literal for int() with base 10: ''Vikings'' demo/test_foo.py:4: ValueError =========================== short test summary info ============================ FAIL demo/test_foo.py::test_spam SKIP [1] .../demo/conftest.py:12: [NOTRUN] ignoring everything but ValueError ===================== 1 failed, 1 skipped in 0.07 seconds ======================

Utilicé la bandera -ra para test_ham que test_ham se omitió ahora.

Si reemplaza la llamada pytest.skip() con pytest.xfail("[XFAIL] ignoring everything but ValueError") , la prueba se marca como un error esperado:

[ ... ] XFAIL demo/test_foo.py::test_ham reason: [XFAIL] ignoring everything but ValueError [ ... ]

y el uso de outcome.force_result([]) marca como aprobado:

$ pytest -v demo/test_foo.py # verbose to see individual PASSED entries [ ... ] demo/test_foo.py::test_ham PASSED [ 50%]

Depende de usted cuál cree que se adapta mejor a su caso de uso. Para skip() y xfail() el formato de mensaje estándar (prefijado con [NOTRUN] o [XFAIL] ) pero puede usar cualquier otro formato de mensaje que desee.

En los tres casos, pytest no abrirá el depurador para pruebas cuyo resultado haya alterado utilizando este método.

Alteración de declaraciones de afirmación individuales

Si desea alterar las pruebas de assert dentro de una prueba , entonces se está preparando para mucho más trabajo. Sí, esto es técnicamente posible, pero solo reescribiendo el código que Python ejecutará en el momento de la compilación .

Cuando usas pytest , esto ya se está haciendo . Pytest reescribe declaraciones de afirmación para darle más contexto cuando sus afirmaciones fallan ; vea esta publicación de blog para obtener una buena descripción de lo que se está haciendo exactamente, así como el código fuente _pytest/assertion/rewrite.py . Tenga en cuenta que ese módulo tiene más de 1k líneas de largo y requiere que comprenda cómo funcionan los árboles de sintaxis abstracta de Python . Si lo hace, podría monopatch ese módulo para agregar sus propias modificaciones allí, incluyendo rodear la try...except AssertionError: con un try...except AssertionError: handler.

Sin embargo , no puede simplemente deshabilitar o ignorar las afirmaciones de forma selectiva, ya que las declaraciones posteriores podrían depender fácilmente del estado (arreglos de objetos específicos, conjunto de variables, etc.) contra el cual se pretendía proteger una afirmación omitida. Si una afirmación prueba que foo no es None , entonces una afirmación posterior se basa en foo.bar para existir, entonces simplemente se encontrará con un AttributeError allí, etc. Siga con volver a plantear la excepción, si necesita seguir esta ruta .

No voy a entrar en más detalles sobre la reescritura de asserts aquí, ya que no creo que valga la pena seguir esto, no dada la cantidad de trabajo involucrado, y con la depuración post mortem que le da acceso al estado de la prueba en el punto de afirmación falla de todos modos .

Tenga en cuenta que si desea hacer esto, no necesita usar eval() (que de todos modos no funcionaría, assert es una declaración, por lo que necesitaría usar exec() lugar), ni tendría ejecutar la aserción dos veces (lo que puede generar problemas si la expresión utilizada en el estado alterado aserción). En su lugar, incrustaría el nodo ast.Assert dentro de un nodo ast.Try y adjuntaría un controlador de excepción que utilice un nodo ast.Raise vacío ast.Raise volver a generar la excepción que se detectó.

Usar el depurador para omitir declaraciones de aserción.

El depurador de Python realmente le permite omitir declaraciones , utilizando el comando j / jump . Si sabe de antemano que una afirmación específica fallará, puede usarla para omitirla. Puede ejecutar sus pruebas con --trace , que abre el depurador al comienzo de cada prueba , luego emite una j <line after assert> para omitirlo cuando el depurador está en pausa justo antes de la afirmación.

Incluso puedes automatizar esto. Usando las técnicas anteriores, puede crear un complemento de depurador personalizado que

  • usa el gancho pytest_testrun_call() para detectar la excepción AssertionError
  • extrae el número de línea ''ofensiva'' de la línea del rastreo, y quizás con algún análisis de código fuente determina los números de línea antes y después de la afirmación requerida para ejecutar un salto exitoso
  • ejecuta la prueba nuevamente , pero esta vez usando una subclase Pdb que establece un punto de interrupción en la línea antes de la afirmación, y ejecuta automáticamente un salto al segundo cuando se alcanza el punto de interrupción, seguido de una c continuar.

O, en lugar de esperar a que falle una aserción, puede automatizar el establecimiento de puntos de interrupción para cada aserción encontrada en una prueba (nuevamente utilizando el análisis del código fuente, puede extraer trivialmente números de línea para un ast.Assert Nodos de ast.Assert en un AST de la prueba) ejecute la prueba afirmada utilizando los comandos con secuencia de comandos del depurador y use el comando de jump para omitir la afirmación misma. Tendrías que hacer una compensación; ejecute todas las pruebas bajo un depurador (que es lento ya que el intérprete tiene que llamar a una función de rastreo para cada declaración) o solo aplique esto a las pruebas fallidas y pague el precio de volver a ejecutar esas pruebas desde cero.

Tal complemento sería mucho trabajo para crear, no voy a escribir un ejemplo aquí, en parte porque no encajaría en una respuesta de todos modos, y en parte porque no creo que valga la pena . Simplemente abría el depurador y hacía el salto manualmente. Una afirmación errónea indica un error en la prueba en sí o en el código bajo prueba, por lo que también puede centrarse en depurar el problema.


Puede lograr exactamente lo que desea sin absolutamente ninguna modificación de código con pytest --pdb .

Con tu ejemplo:

import pytest def test_abc(): a = 9 assert a == 10, "some error message"

Ejecutar con --pdb:

py.test --pdb

collected 1 item test_abc.py F >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> def test_abc(): a = 9 > assert a == 10, "some error message" E AssertionError: some error message E assert 9 == 10 test_abc.py:4: AssertionError >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > /private/tmp/a/test_abc.py(4)test_abc() -> assert a == 10, "some error message" (Pdb) p a 9 (Pdb)

Tan pronto como una prueba falla, puede depurarla con el depurador de python incorporado. Si ha terminado de depurar, puede continue con el resto de las pruebas.


Si está utilizando PyCharm, puede agregar un punto de interrupción de excepción para pausar la ejecución cada vez que falla una aserción. Seleccione Ver puntos de interrupción (CTRL-SHIFT-F8) y agregue un controlador de excepciones en aumento para AssertionError. Tenga en cuenta que esto puede ralentizar la ejecución de las pruebas.

De lo contrario, si no le importa hacer una pausa al final de cada prueba fallida (justo antes de que se produzca un error) en lugar de en el momento en que falla la afirmación, entonces tiene algunas opciones. Sin embargo, tenga en cuenta que en este punto ya podrían haberse ejecutado varios códigos de limpieza, como el cierre de archivos que se abrieron en la prueba. Las opciones posibles son:

  1. Puede decirle a pytest que lo coloque en el depurador de errores utilizando la opción --pdb .

  2. Puede definir el siguiente decorador y decorar cada función de prueba relevante con él. (Además de registrar un mensaje, también puede iniciar un pdb.post_mortem en este punto, o incluso un code.interact interactivo. code.interact con los locales del marco donde se originó la excepción, como se describe en esta respuesta ).

from functools import wraps def pause_on_assert(test_func): @wraps(test_func) def test_wrapper(*args, **kwargs): try: test_func(*args, **kwargs) except AssertionError as e: tkinter.messagebox.showinfo(e) # re-raise exception to make the test fail raise return test_wrapper @pause_on_assert def test_abc() a = 10 assert a == 2, "some error message"

  1. Si no desea decorar manualmente cada función de prueba, puede definir un dispositivo de uso automático que inspeccione sys.last_value :

import sys @pytest.fixture(scope="function", autouse=True) def pause_on_assert(): yield if hasattr(sys, ''last_value'') and isinstance(sys.last_value, AssertionError): tkinter.messagebox.showinfo(sys.last_value)


Una solución simple, si está dispuesto a usar Visual Studio Code, podría ser usar puntos de interrupción condicionales .

Esto le permitiría configurar sus afirmaciones, por ejemplo:

import pytest def test_abc(): a = 10 assert a == 10, "some error message"

Luego agregue un punto de interrupción condicional en su línea de afirmación que solo se romperá cuando su afirmación falle: