No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso de Flask

Curso de Flask

Bernardo Cassina

Bernardo Cassina

Pruebas básicas con Flask-testing

20/36
Recursos

La etapa de pruebas se denomina testing y se trata de una investigación exhaustiva, no solo técnica sino también empírica, que busca reunir información objetiva sobre la calidad de un proyecto de software, por ejemplo, una aplicación móvil o un sitio web.

El objetivo del testing no solo es encontrar fallas sino también aumentar la confianza en la calidad del producto, facilitar información para la toma de decisiones y detectar oportunidades de mejora.

Aportes 57

Preguntas 29

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

No se si a alguien le sucedió el mismo error que a mí.

La función self.assertRedirects extrae el atributo location del primer parámetro (“response”) , y del segundo revisa el netloc, que seria para ambos parámetros
localhost

Para esto usa la función urlparse, pero no toma el netloc, solo el path ‘/’ en index y ‘/hello’ .

Como no encuentra el netloc en el segundo parametro, le añade ‘localhost’, por lo que la comparación queda mal.

Posiblemente esto no ocurría en las versiones anteriores de Flask-Testing.

Así que modifique la función del modulo. Ya que en Visual Code se puede acceder a la función directamente ubicando el puntero en esta y presionando CTRL.

Aquí esta el código corregido. Sé que el código puede ser más prolijo pero priorice claridad de uso sobre limpieza.

Aquí esta el repositorio original:

jarus/flask-testing

https://pastebin.com/7CLNGK0h

Para probar si un mensaje fue flasheado pueden usar el método assert_message_flashed()

def test_user_registered_flashed_message(self):
    fake_form = {
        'username': 'vijoin',
        'password': '123456'
    }
    self.client.post(url_for('index'), data=fake_form)
    message = 'User registered successfully'
    self.assert_message_flashed(message)

Este método solo recibe el mensaje que quieres validar. Los mensajes flasheados ya están en el atributo self.flashed_messages luego de realizar el request

Esta clase deberia de estar realizada con pytest y no con flask-testing. Quizá no tan conveniente en algunas cosas pero definitavamente más standar y future proof

Esto es lo que sucede cuando no se usa un control de versiones, para que tiene su requirements.txt si tiene version infinita? para saver que version de werkzeug tiene el prof 😕

La verdad nunca llegué a programar para una página web, empecé con Inteligencia artificial y ahora quise seguir con este mundo para ver como me va

Mi solución para el tercer y quinto test:

def test_index_redirects(self):
    from urllib.parse import urlparse
    response = self.client.get(url_for('index'))
    parsed_location = urlparse(response.location)
    self.assertEqual(parsed_location.path, url_for('hello'))

def test_hello_post(self):
    fake_form = {
        'username': 'fake',
        'password': 'fake-password'
    }
    response = self.client.post(url_for('hello'), data=fake_form)
    parsed_location = urlparse(response.location)
    self.assertEqual(parsed_location.path, url_for('index'))

Con la función urlparse de Python analizamos la URL del objeto response.location para comparar solo la ruta, ignorando el nombre del servidor y cualquier otro parámetro de la URL.

Hago las clases suponiendo entender algo de lo que hago pero no es el caso, en resumen, ni siquiera se si vaya a seguir esto, sería inútil…

SOLUCIÓN PARA EL ERROR:

FAIL: test_index_redirects (tests_baseMainTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\Documents\Cursos Back End Developer\CRUD_TO_DO_LIST\tests\tests_base.py", line 23, in test_index_redirects
    self.assertRedirects(response, url_for('hello'))
  File "C:\Users\\DocumentsCursos Back End Developer\CRUD_TO_DO_LIST\venv\lib\site-packages\flask_testing\utils.py", line 314, in assertRedirects
    self.assertEqual(response.location, expected_location, message)
AssertionError: '/hello' != '/localhost/hello'
- /hello
+ httocalhost/hello

EXPLICACIÓN:
Este error se ocasiona en el tercer test

La función self.assertRedirects extrae el atributo location del primer parámetro (“response”) , y del segundo revisa el netloc, que seria para ambos parámetros
localhost
Para esto usa la función urlparse, pero no toma el netloc, solo el path ‘/’ en index y ‘/hello’ .
Como no encuentra el netloc en el segundo parametro, le añadi ‘localhost’, por lo que la comparación queda mal.
Posiblemente esto no ocurría en las versiones anteriores de Flask-Testing.
Así que modifique la función del modulo. Ya que en Visual Code se puede acceder a la función directamente ubicando el puntero en esta y presionando CTRL.
Aquí esta el código corregido. Sé que el código puede ser más prolijo pero priorice claridad de uso sobre limpieza.
Aquí esta el repositorio original:
jarus/ flask-testing

DADA LA EXPLICACIÓN, VIENE LA SOLUCIÓN:

<h4>En el tercer test subraya **assertRedirects** con click derecho despliega las opciones, escoge la primera "Go to definition" y en la línea 304 en utilspuntopy cambia elif parts punto netloc — por — if parts punto path --</h4> 

CORRE LOS TESTS Y SIGUE CON TU VIDA 😎😁

Para correr pruebas desde Visual Studio Code solo haría falta agregar en el documento launch.json la siguiente configuración:

        {
            "name": "Python: Flask-testing",
            "type": "python",
            "request": "launch",
            "module": "flask",
            "env": {
                "FLASK_APP": "main.py",
                "FLASK_ENV": "development",
                "FLASK_DEBUG": "1"
            },
            "args": [
                "test"
            ],
            "jinja": true
        },

Y solo bastaría correr desde esta nueva configuración. Espero les sirva de algo 😉.

Para los que están usando Windows ejecuten esto el lugar del export el set

$env:FLASK_APP = "main.py"

sino me está redirigiendo a index, cómo puedo saber a donde me está mandando?

Hola, no se si alguien me pueda ayudar, en el test del POST me sale el siguiente error:

FAIL: test_hello_post (test_base.MainTest)

Traceback (most recent call last):
File “/home/andresm/Escritorio/platzi-flask/tests/test_base.py”, line 33, in test_hello_post
self.assertRedirects(response, url_for(‘index’))
File “/home/andresm/Escritorio/platzi-flask/venv/lib/python3.6/site-packages/flask_testing/utils.py”, line 308, in assertRedirects
self.assertTrue(response.status_code in valid_status_codes, message or not_redirect)
AssertionError: False is not true : HTTP Status 301, 302, 303, 305, 307 expected but got 200

No se si sea porque cuando se hace el post luego se hace un get del index… este es el comportamiento del servidor cuando se hace el post:

127.0.0.1 - - [06/May/2020 17:05:50] “POST /hello HTTP/1.1” 302 -
127.0.0.1 - - [06/May/2020 17:05:50] “GET / HTTP/1.1” 302 -
Pepe
127.0.0.1 - - [06/May/2020 17:05:50] “GET /hello HTTP/1.1” 200 -
127.0.0.1 - - [06/May/2020 17:05:50] “GET /favicon.ico HTTP/1.1” 200 -

No se si a alguno le ha pasado.

para poder hacer las pruebas como el profesor deben de agregar a su archivo de requerimientos.txt flask-testing==0.7.1 ya que con la versión más actual no acepta esas funciones. y ya todo corre sin problemas.

AssertionError: False is not true : HTTP Status 301, 302, 303, 305, 307 expected but got 200 obtuve este error corriendo el ultimo test.

Este deberia ser el contenido del requirements.txt para que no salga el error al momento de llamar al assertRedirect y que todos los test corran bien. Tambien sirve tener Python 3.8 o 3.7

click==7.0
dominate==2.3.5
Flask==1.0.2
Flask-Bootstrap==3.3.7.1
Flask-Testing==0.7.1
Flask-WTF==0.14.2
itsdangerous==1.1.0
Jinja2==2.10
MarkupSafe==1.1.1
visitor==0.1.3
Werkzeug==0.14.1
WTForms==2.2.1

Recuerden siempre hacer pip freeze >> requirements.txt al terminar un proyecto para q estas cosas no los compliquen ya que las versiones avanzan y no todas las bibliotecas llegan a seguir siendo mantenidas

Para quienes tengan al inicio, el siguiente error
TypeError: discover() missing 1 required positional argument: ‘start_dir’
Para el uso de la función discover se les realizan, los cambios en el código fueron los siguientes:
loader = unittest.TestLoader()
tests= loader.discover(’.’)
u=unittest.TextTestRunner()
u.run(tests)

😃

No entiendo muy bien el tema de test. ¿Cuál es el objetivo? ¿Qué información y como utilizo la información en caso de no pasar un test?

Cuando ejecuto el comando, mi máquina empieza como analizar todo los archivos, la verdad no se que esta pasando. A alguien mas se le pasó?

es una lista interminable, pero esto es de lo primero

⚡ flask test              
struct.pack:  b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00'
DEPRECATION WARNING: The system version of Tk is deprecated and may be removed in a future release. Please don't rely on it. Set TK_SILENCE_DEPRECATION=1 to suppress this warning.
test_sqlite: testing with version '2.6.0', sqlite_version '3.30.1'
__phello__.foo
_bootlocale
_collections_abc
_compat_pickle
_compression
_dummy_thread
_markupbase
_osx_support
_py_abc
_pydecimal
_pyio
_sitebuiltins
_strptime
_sysconfigdata_m_darwin_darwin
_threading_local
_weakrefset
abc
aifc

Tuve que devolverme a esta clase, sin entender por que al hacer el Test Driven Development en todas las clases subsiguientes, nunca me funcionaron los test. Para darme cuenta de la importancia que tiene exportar la variable export

FLASK_APP=main.py

Creo que la confusión parte de que en determinado momento exporte la variable

FLASK_APP=test_base.py 

Esta parte se me había complicado, por esta pequeña sutileza.

Ejecute flask test y empezó a ejecutar una lista interminable de comprobaciones. Aún esta corriendo tras 20 minutos de ejecución, no era que no tenía que correr nada?

Para el tercer y quinto test: ```js def test_index_redirects(self): response = self.client.get(url_for('index')) self.assertEqual(response.location, url_for('hello')) def test_hello_post(self): fake_form = { 'username': 'fake', 'password': 'fake-password' } response = self.client.post(url_for('hello'), data= fake_form) self.assertTrue(response, url_for('index')) ```

Aqui dejo por si alguien necesita, Si tienes un error en el 3 test:

… line 314, in assertRedirects
self.assertEqual(response.location, expected_location, message)
AssertionError: ‘/hello’ != ‘htt_p://localhost:5000/hello’

  • /hello
  • htt_p://localhost:5000/hello

Encontre esta solucion modificando en la ruta index:

@app.route('/')
def index():
    user_ip = request.remote_addr
    response = make_response(redirect(url_for('hello', _external=True)))
    session['user_ip'] = user_ip
    return response

Y modificando el test 3:

def test_index_redirects(self):
	response = self.client.get(url_for('index'))
	self.assertRedirects(response, url_for('hello', _external=True))

El último test me arrija un error.
Mi código es de de abajo. No encuentro qué debo arreglar.

    def test_hello_post(self):
        """Cuando enviamos un post con la forma, debemos obtener un index"""
        fake_form={
            'username': 'fake',
            'password': 'fake-password'
                }
        response = self.client.post(url_for('hello'), data=fake_form)

        self.assertRedirects(response, url_for('index'))

Y el error:

flask test
...F.
======================================================================
FAIL: test_hello_post (test_base.MainTest)
Cuando enviamos un post con la forma, debemos obtener un index
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\jorge\OneDrive\Documents\Platzi\Curso_de_Flask\tests\test_base.py", line 42, in test_hello_post
    self.assertRedirects(response, url_for('index'))
  File "c:\users\jorge\onedrive\documents\platzi\curso_de_flask\venv\lib\site-packages\flask_testing\utils.py", line 313, in assertRedirects
    self.assertTrue(response.status_code in valid_status_codes, message or not_redirect)
AssertionError: False is not true : HTTP Status 301, 302, 303, 305, 307 expected but got 200

----------------------------------------------------------------------
Ran 5 tests in 0.058s

FAILED (failures=1)

Un poco más de flask-testing: https://pythonhosted.org/Flask-Testing/

Me encanto esta clase la verdad con esto se defiende uno total con unit test.
Utilizando este modelo seria necesario crear mocks para probar con set de datos supuestos que vienen ejemplo de una DB .
Seria genial material sobre este tema.

from flask_testing import TestCase
from flask import current_app,url_for
from main import app

class MainTest(TestCase):
  def create_app(self):
    app.config['TESTING']=True
    app.config['WTF_CSRF_ENABLED']=False
    return app

  def test_app_exists(self): #app existe
    self.assertIsNotNone(current_app) 
  
  def test_app_in_test_mode(self): #app esta en testing
    self.assertTrue(current_app.config['TESTING'])
  
  def test_index_redirects(self):  #verificar que el index si redirige a hello
    response= self.client.get(url_for('index'))
    self.assertRedirects(response, url_for('hello'))

  def test_hello_get(self): #hello regresa 200 cuando se hace get
    response= self.client.get(url_for('hello'))
    self.assert200(response)
  
  def test_hello_post(self): #hello verificacion de  post
    fake_form={
      'username':'fake' ,
      'password': 'fake-passw'
    }
    response = self.client.post(url_for('hello'), data=fake_form)
    self.assertRedirects(response,url_for('index'))```

Muy contento con este apartado de testing en flask, saludos desde Py.

UnitTest Busca todos los archivos que comienza con la palabra test

El self.client que me permite hacer http_requests viene en el TestCase?

Por si les sale este error:

ERROR: test_base (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: test_base

Se puede solucionar agregando las siguientes líneas en el main.py:

import werkzeug
werkzeug.cached_property = werkzeug.utils.cached_property```

Mi solución a los errores:from flask\_testing import TestCasefrom flask import current\_app, url\_forfrom flask\_test import app class MainTest(TestCase):    def create\_app(self):        app.config\['TESTING'] = True        app.config\['WTF\_CSRF\_ENABLE'] = False        return app        #verificar la aplicación    def test\_app\_exits(self):        self.assertIsNotNone(current\_app)     #verificar que la app está en test    def test\_app\_in\_test(self):        self.assertTrue(current\_app.config\['TESTING'], True)        #verificar que "/" redirige a /index    def test\_home\_redirect(self):        response = self.client.get(url\_for('home'))        # Normaliza la comparación quitando el dominio y comparando solo la parte relativa        self.assertEqual(response.location.split('//')\[-1], url\_for('index'))     #verificar respuesta 200    def test\_index\_get(self):        response = self.client.get(url\_for('index'))         self.assert200(response)        #verificar post    def test\_index\_post(self):        fake\_form = {            'user\_name': 'fake',            'password': 'fake-password'        }        response = self.client.post(url\_for('index'), data = fake\_form)        self.assert200(response)        ```js from flask_testing import TestCase from flask import current_app, url_for from flask_test import app class MainTest(TestCase): def create_app(self): app.config['TESTING'] = True app.config['WTF_CSRF_ENABLE'] = False return app #verificar la aplicación def test_app_exits(self): self.assertIsNotNone(current_app) #verificar que la app está en test def test_app_in_test(self): self.assertTrue(current_app.config['TESTING'], True) #verificar que "/" redirige a /index def test_home_redirect(self): response = self.client.get(url_for('home')) # Normaliza la comparación quitando el dominio y comparando solo la parte relativa self.assertEqual(response.location.split('//')[-1], url_for('index')) #verificar respuesta 200 def test_index_get(self): response = self.client.get(url_for('index')) self.assert200(response) #verificar post def test_index_post(self): fake_form = { 'user_name': 'fake', 'password': 'fake-password' } response = self.client.post(url_for('index'), data = fake_form) self.assert200(response) ```
Mi solución a los errores: `from flask_testing import TestCasefrom flask import current_app, url_forfrom flask_test import app` `class MainTest(TestCase):    def create_app(self):        app.config['TESTING'] = True        app.config['WTF_CSRF_ENABLE'] = False        return app        #verificar la aplicación    def test_app_exits(self):        self.assertIsNotNone(current_app)` `    #verificar que la app está en test    def test_app_in_test(self):        self.assertTrue(current_app.config['TESTING'], True)        #verificar que "/" redirige a /index    def test_home_redirect(self):        response = self.client.get(url_for('home'))        # Normaliza la comparación quitando el dominio y comparando solo la parte relativa        self.assertEqual(response.location.split('//')[-1], url_for('index'))` `    #verificar respuesta 200    def test_index_get(self):        response = self.client.get(url_for('index'))` `        self.assert200(response)        #verificar post    def test_index_post(self):        fake_form = {            'user_name': 'fake',            'password': 'fake-password'        }        response = self.client.post(url_for('index'), data = fake_form)        self.assert200(response) `      
Si te salió error, reemplaza el: ```js self-assertRedirects(response, url_for('hello') ```Por: ```js self.assertEqual(response.location, '/hello') ```La explicación más a profundidad te la pueden dar en los comentarios más adelante.

El test_index_redirects no me quería funcionar porque estaba comparando la url completa y no solo el endpoint /hello.

Consultando encontré esto, url_parse se utiliza para descomponer las URLs en partes como la ruta y la consulta. Luego, se comparan las partes relevantes para asegurarte de que las URLs de redirección coincidan sin considerar el dominio (localhost) ni el esquema (http o https). Esto hace que la prueba sea más independiente del entorno de ejecución y se enfoque en el comportamiento de redirección esperado.

from werkzeug.urls import url_parse 

    def test_index_redirects(self):
        response = self.client.get(url_for("index"))
        # Obtener las rutas absolutas de las URLs de redirección
        expected_url = url_parse(url_for("hello", _external=True))
        actual_url = url_parse(response.location)

        # Verificar que las rutas sean iguales
        self.assertEqual(expected_url.path, actual_url.path)
        self.assertEqual(expected_url.query, actual_url.query) 

Realmente creo que el navbar.html tiene un poco de codigo basura, no se si me equivoque pero obtengo el mismo resultado con el siguiente navbar:

<div class="navbar navbar-inverse" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <a class="navbar-brand" href="{{ url_for('index') }}">
            <img src="{{ url_for('static', filename='images/Erizo_Mimido.jpg') }}" alt="Erizo mimido jaja">
            </a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="https://flask.palletsprojects.com/en/2.3.x/" target="_blank"> Flask Documentation</a></li>
            </ul>
        </div>
    </div>
</div>


Realmente no sé cuál es el público objetivo del docente en este curso, empieza a hablar de cosas que no ha explicado y termina uno copiando código por inercia…
Muy mal preparado este curso.

A la hora de ejecutar los tests, vamos a crear una aplicación del mismo modo que se crea cuando se lanza el servidor de Flask. Esta aplicación tomará los parámetros de configuración definidos en el fichero config/testing.py. Ábrelo y comprueba si su contenido es este:

from .default import *
# Parámetros para activar el modo debug
TESTING = True
DEBUG = True
APP_ENV = APP_ENV_TESTING
WTF_CSRF_ENABLED = False

Si te falta algún parámetro, añádelo de manera que tu fichero quede similar al que te muestro arriba. El significado de cada uno de los parámetros es el siguiente:
TESTING Deshabilita la captura de errores durante el manejo de peticiones para obtener mejores informes de error en los tests
DEBUG Activa el modo debug
APP_ENV Nombre del entorno de ejecución. En este caso ‘testing’
WTF_CSRF_ENABLED Lo establecemos a False para deshabilitar la protección CSRF durante los tests

Por otro lado, para los tests vamos a utilizar una base de datos diferente a la de desarrollo. Esto lo haremos debido a que cada vez que se ejecuta un test, crearemos y borraremos las tablas de la base de datos (para asegurar la correcta ejecución de los tests). De este modo, usando una base de datos diferente, podremos tener siempre nuestros datos de prueba a salvo.

La cadena de conexión a la base de datos la definiremos en el fichero instance/config-testing.py. Abre este fichero y añade la cadena de conexión (con los datos de tu propia base de datos):

SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:testing@localhost:5432/miniblog_test'

Este video me ayudó muchísimo a comprender un poco más los CLI, en realidad son un concepto sencillo: https://www.youtube.com/watch?v=7yJbMfQMvRQ&t=3030s&ab_channel=codigofacilito

Genial!!!

👍👍👍

Si tienes este Error:

ImportError: Start directory is not importable: 'tests’

Debes cambiar esto:

@app.cli.command()
def test():
    tests = unittest.TestLoader().discover('tests')
    unittest.TextTestRunner.run(tests)

Por esto:

@app.cli.command()
def test():
    tests = unittest.TestLoader().discover('tests')
    u = unittest.TextTestRunner() 
    u.run(tests)

Esto sucede porque primero debes inicializar una instancia de la clase TextTestRunner y luego ahí si correr su método Run()

Para que me funcionara el test tuve que crear el archivo

__init__.py

en la carpeta de test

hola tengo problema el implementar flask- test
mi código.

@app.cli.command()
def test():
    tests = unittest.TestLoader().discorver('tests')
    unittest.TextTestRunner().run(tests) ```
 
y ya e corrido el comando "export FLASK_APP=main" y me sigue arrojando este error.
 

Usage: flask [OPTIONS] COMMAND [ARGS]…
Try ‘flask --help’ for help.

Error: No such command ‘test’.```

ModuleNotFoundError: No module named ´flask_testing´

Dentro del directorio test, el archivo test_base.py:

from flask_testing import TestCase

Alguien sabe el por qué?

Tengo el siguiente error:

cli: command line interface

Excelente ya voy entendiendo el tema de los testing

Excelente la etapa de testing.

Todo perfecto hasta el momento

En windows tenia el error :

python - unittest - ImportError: Start directory is not importable

Lo solucione creando un init.py en la carpeta tests

Totalmente Hermoso !

¿el metodo url_for recibe el nombre de la funcion en la cual esta el decorador de app.route? Por ejemplo si el decorador es ‘@app.route(’/hello’)’ y la funcion es def hello_world(), ¿el url_for recibe ‘hello_world’?
Estoy un poco confundido por eso, gracias

Uff Excelso

Se me ejecutan 41 test con el primer uso de flask test

Corri el comando flask test por primera vez y ejecuto 41 tests:

para los que les da el error

AssertionError: False is not true : HTTP Status 301, 302, 303, 305, 307 expected but got 200

a mi me dio ese error porque en mi archivo main yo tenía:

if login_form.validate_on_submit():
        user_name = login_form.user_name.data
        session['user_name'] = user_name

        flash('Nombre de usuario registrado con éxito') #los flashes hay que renderearlos en el html

        return redirect(url_for('index'))

y en el test tenía:

def test_hello_post(self):
        fake_form = {

            'username':'fake',
            'password': 'fake-pasword',

        }
        response = self.client.post(url_for('hello'), data=fake_form)
        self.assertRedirects(response, url_for('index'))

es dificil de ver, pero el formulario tiene que ser identico en el archivo main como en el archivo testing:
la solución fue cambiar ‘username’ del archivo testing a ‘user_name’

El CSRF (del inglés Cross-site request forgery o falsificación de petición en sitios cruzados) es un tipo de exploit malicioso de un sitio web en el que comandos no autorizados son transmitidos por un usuario en el cual el sitio web confía.