0

I have created a package which uploads a data to some storage using Azure AD access token, now I want to write test cases for the code, as I'm not aware of writing test cases have tried few. Can anyone help here, below is the code for my package.

__init__.py file

import json
import requests
import sys
from data import Data
import datetime
from singleton import singleton


@singleton
class CertifiedLogProvider:
    def __init__(self, client_id, client_secret):
        self.client_id=client_id
        self.client_secret= client_secret
        self.grant_type="client_credentials"
        
        self.resource="*****"
        self.url="https://login.microsoftonline.com/azureford.onmicrosoft.com/oauth2/token"
        self.api_url="http://example.com"
        self.get_access_token()
        
    def get_access_token(self)-> None:
        
        data={'client_id':self.client_id,'client_secret':self.client_secret,
               'grant_type':self.grant_type,'resource':self.resource}
        response = requests.post(self.url, data=data)
        
        if response.status_code == 200:
            
            self.token_dict=response.content
            self.access_token = response.json()["access_token"]
        else:
            print(f"An Error occurred: \n {response.text}")
        

    def is_expired(self) -> bool:
        
        token_dict=json.loads(self.token_dict.decode('utf-8'))
        if int(datetime.datetime.now().timestamp()) > int(token_dict['expires_on']):
            return True
        else:
            return False
        

    def send_clp_data(self,payload:dict):
        obj=Data()
        data=obj.data
        data['event_metric_body']=payload
        
        if self.is_expired() is True:
            self.get_access_token()

        headers={"Authorization": "Bearer {}".format(self.access_token),
                      "Content-Type": "application/json",}
        
        
        response = requests.post(self.api_url,data=json.dumps(data), headers=headers)
        if response.status_code == 201:
            print('Data uploaded successfully')
        else:
            print(f"An Error occurred: \n {response.text}")

singleton.py

def singleton(class_):
    instances = {}
    def getinstance(*args, **kwargs):
        if class_ not in instances:
            instances[class_] = class_(*args, **kwargs)
        return instances[class_]
    return getinstance

data.py

Contains data which is static

test.py

import json
import unittest
from unittest import TestCase
from unittest.mock import patch
import requests
from unittest.mock import MagicMock
from __init__ import CertifiedLogProvider
import pytest

class MyTestCase(TestCase):
    
    def test_can_construct_clp_instance(self):
        object= CertifiedLogProvider(1,2)
        

    @patch('requests.post')
    def test_send_clp_data(self, mock_post):
        info={"test1":"value1", "test2": "value2"}
        response = requests.post("www.clp_api.com", data=json.dumps(info), headers={'Content-Type': 'application/json'})
        mock_post.assert_called_with("www.clp_api.com", data=json.dumps(info), headers={'Content-Type': 'application/json'})

    
            

if __name__ == '__main__':
    unittest.main()

How can we test boolean method and method containing requests?

zuana
  • 23
  • 5
  • Mocking requests: https://stackoverflow.com/questions/15753390/how-can-i-mock-requests-and-the-response – Tzane Feb 16 '22 at 11:11
  • Thanks that really helps, but I'm kind of bit confused as this is the first time I'm trying test code. I did understand a bit but my concern is lets say if we wanna write test case for get_access_token then how do we get the data_headers to test for it, if you can help me with that I would definitely get an idea and do others. – zuana Feb 17 '22 at 10:21

1 Answers1

0

Testing is_expired() becomes easier if you create a Token class:

class Token:
    def __init__(self, token_dict: dict) -> None:
        self.token_dict = token_dict

    def __str__(self) -> str:
        return self.token_dict['access_token']

    def is_expired(self, now:DateTime=None) -> bool:
        if now is None:
            now = datetime.datetime.now()

        return int(now.timestamp()) > int(self.token_dict['expires_on'])


@singleton
class CertifiedLogProvider:
    def __init__(self, client_id, client_secret):
        self.client_id=client_id
        self.client_secret= client_secret
        self.grant_type="client_credentials"

        self.resource="*****"
        self.url="https://login.microsoftonline.com/azureford.onmicrosoft.com/oauth2/token"
        self.api_url="http://example.com"

        self.access_token = None


    def get_access_token(self) -> Token:
        if self.access_token is None or self.access_token.is_expired():
            self.access_token = self.fetch_access_token()
        return self.access_token


    def fetch_access_token(self) -> Token:
        data={
            'client_id':     self.client_id,
            'client_secret': self.client_secret,
            'grant_type':    self.grant_type,
            'resource':      self.resource,
        }
        response = requests.post(self.url, data=data)
        if response.status_code != 200:
            raise Exception(f"An Error occurred: \n {response.text}")

        return Token(response.json())


    def send_clp_data(self, payload: dict):
        obj=Data()
        data=obj.data
        data['event_metric_body'] = payload

        headers={
            "Authorization": f"Bearer {self.get_access_token()}",
            "Content-Type": "application/json",
        }

        response = requests.post(self.api_url, data=json.dumps(data), headers=headers)
        if response.status_code != 201:
            raise Exception(f"An Error occurred: \n {response.text}")

        print('Data uploaded successfully')

test_token.py

from datetime import datetime

import unittest

from certified_log_provider import Token

class TestToken(unittest.TestCase):

    def setUp(self) -> None:
        self.token = Token({ 'expires_on': datetime.strptime("2022-02-18", "%Y-%m-%d").timestamp() })

    def test_token_is_not_expired_day_before(self):
        self.assertFalse(self.token.is_expired(datetime.strptime("2022-02-17", "%Y-%m-%d")))

    def test_token_is_expired_day_after(self):
        self.assertTrue(self.token.is_expired(datetime.strptime("2022-02-19", "%Y-%m-%d")))
md2perpe
  • 3,372
  • 2
  • 18
  • 22
  • thanks, I'll try the suggestions, but my concern for now is testcode which I'm trying to learn but kind of confused. So if you can help me with testcode for lets say one of the function get_access_token (as per your suggested code fetch_access_token) that would really help me understand and proceed with other methods – zuana Feb 18 '22 at 11:25
  • @NishantChaubey. I made the `isExpired()` method easier to test and added a test. – md2perpe Feb 18 '22 at 11:43
  • Thanks a lot, I'll try it and respond – zuana Feb 18 '22 at 11:52
  • I still have a doubt, if I want to test fetch_access_token, so how do we get the client_id and client_secret there as these user have to pass while creating a object and is secret so can't pass directly, so how can we test it, I tried below, sorry for those many questions but I'm completely beginner to this – zuana Feb 18 '22 at 13:04
  • import unittest from clp import CertifiedLogProvider as cs class TestCerttifiedLogProvider(unittest.TestCase): def test_get_access_token(self): self.url="https://login.microsoftonline.com/azureford.onmicrosoft.com/oauth2/token" data={'client_id':cs.client_id,'client_secret':cs.client_secret, 'grant_type':cs.grant_type,'resource':cs.resource} response=self.get_access_token(self.url,self.data) assert response.status_code == 200 – zuana Feb 18 '22 at 13:04
  • @NishantChaubey. Actually, I have never used unit testing myself. So I'm also a novice. – md2perpe Feb 18 '22 at 14:45
  • @NishantChaubey. When it comes to secrets, put them in some configuration file. The configuration file might be committed to a version control system (like Git), but should go there with empty secret values. Read the secrets from the configuration file also in your tests when needed. The configuration can also go in environment variables. – md2perpe Feb 18 '22 at 14:57
  • I have .env file for keeping secrets and I'm using pydantic for reading env file, but main concern is these two secrets needs to be provided by user when they create a object of the class, anyhow I'll just try to figure it out and many thanks for the help – zuana Feb 18 '22 at 16:29