mercredi 30 décembre 2020

Mock a test with a external api rest in Django Rest Framework

Context

I am developing an API Rest with Django Rest Framework, and some of my endpoints consume an external API Rest to create a model instance, the code works but right now I am trying to test my code but I have very struggled with that. I am a little beginner developing tests and maybe the question is very obvious but, I tried so many things, with the below links but I couldn't achieve it.

Mocking external API for testing with Python

How do I mock a third party library inside a Django Rest Framework endpoint while testing?

https://realpython.com/testing-third-party-apis-with-mocks/

Codes

This is my view

from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from app.lnd_client import new_address
from app.models import Wallet
from api.serializers import WalletSerializer



class WalletViewSet(viewsets.ModelViewSet):
    queryset = Wallet.objects.all()
    serializer_class = WalletSerializer
    lookup_field = "address"

    @action(methods=["post"], detail=False)
    def new_address(self, request):
        response = new_address()
        if "error" in response.keys():
            return Response(
                data={"error": response["error"]}, status=response["status_code"]
            )
        else:
            return Response(response)

    def create(self, request, *args, **kwargs):

        response = self.new_address(self)
        if response.status_code >= 400:
            return response

        serializer = self.get_serializer(data=response.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(
            serializer.data, status=status.HTTP_201_CREATED, headers=headers
        )

This is my client with the call to the external api

import requests
from django.conf import settings
from typing import Dict

endpoint = settings.LND_REST["ENDPOINT"]
endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
endpoint = (
    "https://" + endpoint if not endpoint.startswith("http") else endpoint
)
endpoint = endpoint

auth = {"Grpc-Metadata-macaroon": settings.LND_REST["MACAROON"]}
cert = settings.LND_REST["CERT"]

def new_address() -> Dict:

    try:
        r = requests.get(
            f"{endpoint}/v1/newaddress", headers=auth, verify=cert
        )
        r.raise_for_status()
    except requests.exceptions.HTTPError as errh:
        return {
            "error": errh.response.text,
            "status_code": errh.response.status_code,
        }
    except requests.exceptions.RequestException:
        return {
            "error": f"Unable to connect to {endpoint}",
            "status_code": 500,
        }

    return r.json()

My test is this

from django.urls import reverse
from nose.tools import eq_
from rest_framework.test import APITestCase
from unittest.mock import patch
from rest_framework import status
from .factories import WalletFactory
from app.lnd_client import new_address

class TestWalletListTestCase(APITestCase):
    def setUp(self):
        WalletFactory.create_batch(5)
        self.url = reverse("wallet-list")
        self.wallet_data = WalletFactory.generate_address()

    def test_get_all_wallets(self):
        response = self.client.get(self.url)
        eq_(response.status_code, status.HTTP_200_OK)

    #This is the problem test
    @patch('app.lnd_client.requests.post')
    def test_create_wallet(self,mock_method):
        mock_method.return_value.ok = True
        response = self.client.post(self.url)
        eq_(response.status_code, status.HTTP_201_CREATED)

Aucun commentaire:

Enregistrer un commentaire