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 55

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 (鈥渞esponse鈥) , 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 鈥榣ocalhost鈥, 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

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 馃槙

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

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 (鈥渞esponse鈥) , 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 鈥榣ocalhost鈥, 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 馃槈.

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(鈥榠ndex鈥))
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] 鈥淧OST /hello HTTP/1.1鈥 302 -
127.0.0.1 - - [06/May/2020 17:05:50] 鈥淕ET / HTTP/1.1鈥 302 -
Pepe
127.0.0.1 - - [06/May/2020 17:05:50] 鈥淕ET /hello HTTP/1.1鈥 200 -
127.0.0.1 - - [06/May/2020 17:05:50] 鈥淕ET /favicon.ico HTTP/1.1鈥 200 -

No se si a alguno le ha pasado.

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

$env:FLASK_APP = "main.py"

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: 鈥榮tart_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)

馃槂

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.

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.

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

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鈥 != 鈥榟tt_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))
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?

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```

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 鈥榯esting鈥
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 鈥榝lask --help鈥 for help.

Error: No such command 鈥榯est鈥.```

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 鈥榟ello_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 鈥榰sername鈥 del archivo testing a 鈥榰ser_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.