Python Django: desarrollo basado en pruebas de API web utilizando DRF y Docker

Vamos a crear una API web de lista de tareas pendientes usando Django rest framework , docker y también vamos a escribir diferentes pruebas para diferentes funcionalidades en nuestro código usando desarrollo basado en pruebas , pero primero veamos cuáles son los requisitos previos para este proyecto.

Requisitos previos:

  1. Docker instalado en su sistema local
  2. Conocimientos básicos de python 3.0
  3. Conocimientos básicos de Django 3.0

Ahora que estamos listos para comenzar, aprendamos más sobre la ventana acoplable y el desarrollo basado en pruebas (TDD) y por qué deberíamos usarlos.

ventana acoplable:

Docker es una plataforma de contenedorización de código abierto para automatizar la implementación de aplicaciones como contenedores portátiles y autosuficientes que pueden ejecutarse en la nube o en las instalaciones.

Considere una situación en la que un ingeniero de software escribe un código y lo envía para probarlo, pero ese código no se ejecutará en el entorno local del probador porque no se cumplen todas las dependencias, este problema se puede eliminar simplemente usando Docker.

Desarrollo dirigido por pruebas (TDD):

Test-Driven Development es una práctica de desarrollo de software donde el enfoque está en escribir pruebas unitarias antes de escribir el código real, es un enfoque iterativo que combina la programación, la creación de pruebas unitarias y la refactorización.

Prueba unitaria : las pruebas unitarias son la prueba para probar diferentes funcionalidades en nuestro código

Tres pasos clave para escribir pruebas:

  1. Configuración: creación de funciones de muestra que se utilizarán en diferentes pruebas
  2. Ejecución: Llamar al código que se está probando
  3. Aseveración: comparar los resultados con los resultados esperados.

Ahora pasemos a la parte de construcción real.

Creando el proyecto y configurando el Dockerfile:

Siga los pasos a continuación para crear un proyecto y configurar el Dockerfile.

Paso 1: Crear Dockerfile.

  • Cree un nombre de archivo Dockerfile. Un Dockerfile es un archivo que contiene instrucciones para construir nuestra imagen acoplable.

Paso 2: Rellene Dockerfile.

FROM python:3.8-alpine
ENV PYTHONBUFFERED=1 
  • La primera línea de nuestro dockerfile especifica la imagen de python existente sobre la cual vamos a construir nuestra imagen. Vamos a usar python: 3.8 alpine image como nuestra imagen base, si lo desea, puede crear su propia imagen base, la razón para usar alpine es liviano y lleva menos tiempo construirlo
  • A continuación, configure el entorno pythonbuffered como 1, esto evita el almacenamiento en búfer de salida y reduce el tiempo de compilación
COPY ./requirements.txt /requirements.txt
RUN pip3 install -r requirements.txt
  • Este comando significa copiar nuestros requisitos locales.txt a nuestros requisitos de imágenes.txt
  • Ejecutando nuestro archivo de requisitos para instalar todas las dependencias
RUN mkdir /app
WORKDIR /app
COPY ./app /app
RUN adduser -D user
USER user
  • Mantendremos nuestro directorio de trabajo llamado aplicación y copiaremos el directorio de la aplicación local a la imagen.
  • Crear un nuevo usuario para nuestra imagen para que no usemos root como nuestro usuario principal es una práctica de seguridad

Paso 3: Cree una carpeta de Proyecto.

  • Cree una carpeta llamada aplicación además de Dockerfile. Esta será nuestra carpeta de proyecto.

Paso 4: Crear requisitos.txt.

  •  Este archivo contiene las dependencias requeridas para nuestro proyecto, agregue lo siguiente a sus requisitos.txt
Django==3.2.12
djangorestframework==3.13.1

Paso 5: Cree la imagen de Docker.

$ docker build .

Ejecute el comando anterior, su imagen acoplable debe comenzar a construirse

Paso 6: Cree docker-compose.

  • Cree el nombre de archivo docker-compose.yml además de Dockerfile. Los archivos de Docker-compose son archivos que contienen diferentes servicios que implementamos en nuestra aplicación y también contienen diferentes instrucciones para configurar y ejecutar esos servicios.
  • Incluya esto en su docker-compose.yml

XML

version: "3"
  
services:
    app:
        build: 
            context: .
    ports:
        - "8000:8000" 
    volumes:
        - ./app:/app
    command: >
        sh -c "python3 manage.py runserver 0.0.0.0:8000"

Nota: estamos usando la versión 3 de docker-compose y nuestra aplicación contiene un servicio llamado aplicación que se almacena en una carpeta llamada aplicación , usaremos el puerto 8000 para ejecutarlo con el comando anterior.

Paso 7: Cree docker-compose.

  • Ejecute el siguiente comando para comenzar a construir su docker-compose.
$ docker-compose build

Paso 8: Crear proyecto Django:

$ docker-compose run app sh -c "django-admin startproject app ."

Paso 9: Ejecute el servidor Django.

  • Ejecute el siguiente comando para iniciar su aplicación Django y diríjase a https://127.0.0.1:8000 para verificar si su aplicación se inició
$ docker-compose up

Paso 10: Cree una aplicación Django llamada «api».

$ cd app
$ docker-compose run app sh -c "python manage.py startapp api"
  • Esto creará una aplicación de API en la que vamos a almacenar todas nuestras operaciones de API CRUD , así como nuestras pruebas para nuestra aplicación de API . Ahora ve a app>settings.py y en install_apps agrega “api”, “rest_framework como nuevas aplicaciones.

Ahora que hemos configurado nuestro proyecto, podemos pasar a escribir el código real, pero primero comprendamos cómo escribir pruebas en python Django.

Reglas para escribir pruebas en Django:

Se deben seguir las siguientes reglas al escribir una prueba de Django:

  • Regla 1: Cree una carpeta llamada tests en la aplicación. En esta carpeta almacenaremos tests delete, el archivo tests.py ya existente
  • Regla 2: Cree __init__.py en la carpeta de pruebas
  • Regla 3: cree diferentes archivos de python para probar diferentes partes, en nuestro caso, por ejemplo, diferentes archivos para probar modelos y vistas. Tenga en cuenta que cada nombre de archivo debe comenzar desde «prueba» para test_model.py.
  • Regla 4: cada archivo debe contener una clase que contenga diferentes pruebas unitarias como funciones que prueben diferentes funcionalidades, además , el nombre de la función debe comenzar desde «prueba», por ejemplo: def test_<nombre de la funcionalidad>()

Pruebas de escritura para el modelo de tarea de prueba:

Siga los pasos a continuación para escribir la prueba para el modelo de tareas:

Paso 1: crea un archivo test_models.

  • En nuestra carpeta de pruebas en la aplicación API, cree un archivo llamado test_models.py que almacenará las pruebas relacionadas con nuestro modelo que vamos a crear.

Paso 2: escribir la primera prueba del modelo.

Python3

from django.test import TestCase
from api import models
  
class ModelTest(TestCase):
  
    def test_tag_model_str(self):
        task = models.Task.objects.create(
            task_name = "Do homework",
            task_description = "Need to complete the homework today",
            task_iscompleted = False
        )
  
        self.assertEqual(str(task), task.task_name)
  1. Arriba Importamos los módulos requeridos, aún no hemos creado nuestros modelos, pero los crearemos en un segundo.
  2. Crea la clase ModelTest() y extiéndela con TestCase
  3. Aquí creamos una prueba unitaria para probar el modelo y escribimos una aserción para verificar si el resultado de salida es el mismo que el resultado esperado usando assertEqual() que compara los dos.

Crear modelo de tareas:

Siga los pasos a continuación para crear el modelo de tareas :

Paso 1: en nuestra aplicación API en el archivo models.py , incluya el siguiente código:

Python3

from django.db import models
  
class Task(models.Model):
    task_name = models.CharField(max_length=255)
    task_description = models.TextField()
    task_time = models.DateTimeField(auto_now_add=True)
    task_iscompleted = models.BooleanField()
  
    def __str__(self):
        return self.task_name
  1. Cree una clase y amplíela con models.Model .
  2. Escriba diferentes campos de clase que representen columnas de nuestro modelo.
  3. Por último, devolvemos task_name para su verificación. 

Paso 2: registre el modelo de tareas.

  • Dirígete a admin.py y registra el modelo usando el siguiente código:

Python3

from django.contrib import admin
from .models import Task
  
admin.site.register(Task)

Paso 3: Migración de cambios. 

  • Es hora de migrar nuestros cambios (asegúrese de migrar cada vez que cambie/cree un modelo). Primero, haz tus migraciones usando 
$ docker-compose run app sh -c "python manage.py makemigrations"
  • Ahora Migrar.
docker-compose run app sh -c "python manage.py migrate"

Paso 4: crear un serializador de modelos.

  • Cree un serializador para nuestro modelo en la aplicación API, cree un archivo llamado serializers.py e incluya los serializadores que se utilizan para validar los datos de solicitud entrantes .

Python3

from rest_framework import serializers
from .models import Task
  
class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = '__all__'

Paso 5: Probar el modelo t ask .

  • Ejecute el siguiente comando:
docker-compose run app sh -c "python manage.py test"
  • Asegúrate de pasar todas las pruebas.
  • Ahora que tenemos nuestro modelo creado, es hora de escribir vistas de API.

Prueba de escritura para vistas de API:

Siga los pasos a continuación para escribir la prueba para la vista API:

Paso 1: Cree un archivo de prueba.

  • Dirígete a la carpeta de pruebas en la aplicación API y crea el archivo test_task_api.py , aquí es donde vamos a escribir pruebas para probar la API.

Paso 2: Escribe la prueba. 

  • El siguiente código contiene pruebas unitarias para diferentes operaciones de API:

Python3

from django.test import TestCase
from rest_framework.test import APIClient
from rest_framework import status
  
from django.urls import reverse
from api.serializers import TaskSerializer
from api.models import Task
  
  
get_tasks_url = reverse('get')
create_url = reverse('create')
  
def update_url(id):
    return reverse('update', args=[id])
  
def delete_url(id):
    return reverse('delete', args=[id])
  
def details_url(id):
    return reverse('details', args=[id])
  
def sample_payload():
    payload = {
        "task_name" : "Do homework",
        "task_description" : "Need to complete the homework today",
        "task_iscompleted" : False
    }
    return Task.objects.create(**payload)

Arriba, importamos los módulos requeridos, luego usamos la función inversa que permite recuperar los detalles de la URL del archivo urls.py a través del valor de nombre proporcionado allí, luego creamos sample_payload, que es un objeto de modelo de tarea de muestra que crea funciones de muestra que hacen que nuestro código sea limpio y rápido.

Python3

class TaskApiTest(TestCase):
      
    def setUp(self):
        self.client = APIClient()
  
    def test_get_task_list(self):
        res = self.client.get(get_tasks_url)
          
        self.assertEqual(res.status_code, status.HTTP_200_OK)
  
    def test_create_task(self):
        payload = {
            "task_name" : "Do homework",
            "task_description" : "Need to complete the homework today",
            "task_iscompleted" : False
        }
  
        res = self.client.post(create_url, payload)
  
        self.assertEqual(res.status_code, status.HTTP_201_CREATED)
  
    def test_update_task(self):
        task = sample_payload()
  
        payload = {
            "task_name" : "Do homework",
            "task_description" : "Need to complete the homework today",
            "task_iscompleted" : True
        }
  
        url = update_url(task.id)
        res = self.client.put(url, payload)
  
        task.refresh_from_db()
        self.assertEqual(res.status_code, status.HTTP_201_CREATED)
      
    def test_delete_task(self):
        task = sample_payload()
        url = delete_url(task.id)
  
        res = self.client.delete(url)
  
        self.assertEqual(res.status_code, status.HTTP_202_ACCEPTED)
  
    def test_task_details(self):
        task = sample_payload()
        url = details_url(task.id)
  
        res = self.client.get(url)
  
        self.assertEqual(res.status_code, status.HTTP_200_OK)

Arriba tenemos diferentes pruebas unitarias para diferentes vistas, primero creamos una clase y la ampliamos con TestCase y escribimos una función para probar diferentes vistas (observe cómo cada nombre de función comienza con prueba), anulamos la función de configuración de TestCase y la usamos para inicializar la clase variables, luego escriba el código y realice las aserciones requeridas para comparar la salida dada con la salida esperada.

Escritura de vistas de API y direcciones URL:

Siga los pasos a continuación para escribir las vistas y URL de la API:

Paso 1: Creación de vistas

  • Dirígete a views.py y escribe el siguiente código. Las siguientes vistas son para manejar diferentes requests y producir el resultado deseado para nuestra API, aquí estamos usando vistas basadas en funciones con el decorador api_view. El núcleo de esta funcionalidad es el decorador api_view, que toma una lista de métodos HTTP a los que debe responder su vista.
  • Para enviar el resultado, usamos Response(). A diferencia de los objetos HttpResponse normales, no crea instancias de objetos Response con contenido representado. En su lugar, pasa datos no procesados, que pueden consistir en cualquier primitivo de Python.

Python3

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Task
from .serializers import TaskSerializer
  
@api_view(['GET'])
def get_all_tasks(request):
    tasks = Task.objects.all().order_by("-task_time")
    serializer = TaskSerializer(tasks, many=True)
    return Response(serializer.data, status=status.HTTP_200_OK)
    
@api_view(['GET'])
def get_task_details(request, pk):
    task = Task.objects.get(id=pk)
    serializer = TaskSerializer(task, many=False)
  
    return Response(serializer.data, status=status.HTTP_200_OK)
  • Aquí importamos los módulos requeridos y escribimos la primera vista para obtener una lista de tareas creadas, primero obtenemos tareas usando el modelo de tareas, luego serializamos datos y devolvemos una respuesta con datos y 200 códigos de estado.
  • Luego creamos una vista para obtener detalles de una sola tarea usando su id.

Python3

@api_view(['POST'])
def create_task(request):
    serializer = TaskSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
  
    return Response(status=status.HTTP_400_BAD_REQUEST)

Arriba hay una vista para crear una nueva tarea, primero necesitamos obtener datos de la solicitud y serializarlos si los datos serializados son válidos, luego simplemente guarde el serializador y devuelva la respuesta deseada.

Python3

@api_view(['PUT'])
def update_task(request, pk):
    task = Task.objects.get(id=pk)
    serializer = TaskSerializer(instance=task, data=request.data)
  
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
  
    return Response(status=status.HTTP_400_BAD_REQUEST)
  
@api_view(['DELETE'])
def delete_task(request, pk):
    task = Task.objects.get(id=pk)
    task.delete()
  
    return Response('Deleted successfully',status=status.HTTP_202_ACCEPTED)
  • Lo siguiente es una actualización y eliminar una tarea en la actualización, aceptar la identificación de la tarea que se actualizará, luego verificar la validez del serializador y devolver una respuesta, lo mismo con la vista de eliminación, obtener la identificación del objeto de la tarea de la solicitud y eliminar la tarea y devolver la respuesta.

Aquí nos completamos con la escritura de vistas.

Paso 2: Configuración de URL.

  • Vaya a urls.py en la aplicación API si no hay una, cree urls.py e incluya el siguiente código:

Python3

from django.urls import path
from . import views
urlpatterns = [
    path('get/', views.get_all_tasks, name="get"),
    path('create/', views.create_task, name="create"),
    path('update/<int:pk>', views.update_task, name="update"),
    path('delete/<int:pk>', views.delete_task, name="delete"),
    path('details/<int:pk>', views.get_task_details, name="details")
]

Arriba hay diferentes patrones de URL para realizar requests a diferentes vistas y obtener una respuesta.

  • Además, vaya a urls.py en la aplicación de la carpeta principal del proyecto, vaya a urls.py y en los patrones de URL agregue:

Python3

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('api.urls'))
]

Aquí, si alguna URL comienza desde api/ , transfiera el control a api.urls.

Examen final:

  • Dirígete a la terminal y ejecuta el siguiente comando:
docker-compose run app sh -c "python manage.py test"
  • Asegúrese de que pasen todas las pruebas:

 

Salidas:

  • Cuando ejecuta el servidor usando el siguiente comando:
$ docker-compose up

 

  • Crear una tarea:

 

  • Obtener todas las tareas:

 

  • Actualización de una tarea:

 

  • Eliminación de una tarea:

 

  • Obtener detalles de la tarea:

 

Conclusión: 

Por lo tanto, aprendimos sobre la creación de API con Django rest framework, docker y desarrollo basado en pruebas. Como la industria del software está creciendo tan rápido, es necesario actualizarse con las nuevas tecnologías.

Publicación traducida automáticamente

Artículo escrito por adityasangave21 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *