1
\$\begingroup\$

I am writing a python module to interact to the ROD (remedy on demand) BMC ticketing product. So far I have only written the log in and log out methods and tests. Before I crack on with the more complex methods I hope someone can review the current test strategy and happy to hear any suggestions for improvements.

rodAPI.py

import requests


class RodApi:
    def __init__(self, fqdn, username, password, token_type="AR-JWT"):
        self.username = username
        self.password = password
        self.fqdn = fqdn
        self.token_type = token_type
        self.login_url = f"https://{fqdn}/api/jwt/login"
        self.logout_url = f"https://{fqdn}/api/jwt/logout"
        self.token = None

    def login(self) -> bool:
        data = {"username": self.username, "password": self.password}
        headers = {"content-type": "application/x-www-form-urlencoded"}
        response = requests.post(self.login_url, data=data, headers=headers)

        print(f"connecting to URL: {self.login_url}")
        print(f"https status code: {response.status_code}")
        self.token = response.text
        if response.status_code == 200:
            return True
        return False

    def logout(self) -> bool:
        headers = {"Authorization": f"{self.token_type} {self.token}"}
        response = requests.post(self.logout_url, headers=headers)

        print(f"connecting to URL: {self.logout_url}")
        print(f"https status code: {response.status_code}")
        if response.status_code == 204:
            print(f"Logged out with a status of: {response.status_code}")
            return True
        else:
            print(f"Error logging out: {response.text}")
            return False

test_rodAPI

import pytest

from rodAPI import RodApi


@pytest.fixture()
def rod_api_instance():
    rod_api = RodApi("example.com", "some_user", "some_password")
    return rod_api


@pytest.fixture()
def rod_api_logged_in_instance(responses, rod_api_instance):
    responses.add(responses.POST, rod_api_instance.login_url, body="some_token_string")
    rod_api_instance.login()
    return rod_api_instance


def test_login_success(responses, rod_api_instance: RodApi):
    responses.add(responses.POST, rod_api_instance.login_url, body="some_token_string")
    result = rod_api_instance.login()
    assert result is True
    assert rod_api_instance.token == "some_token_string"


def test_login_fail(responses, rod_api_instance: RodApi):
    responses.add(responses.POST, rod_api_instance.login_url, status=404)
    result = rod_api_instance.login()
    assert result is False


def test_logout_success(responses, rod_api_logged_in_instance: RodApi):
    responses.add(responses.POST, rod_api_logged_in_instance.logout_url, status=204)
    result = rod_api_logged_in_instance.logout()
    assert result is True


def test_logout_fail(responses, rod_api_logged_in_instance: RodApi):
    responses.add(responses.POST, rod_api_logged_in_instance.logout_url, status=404)
    result = rod_api_logged_in_instance.logout()
    assert result is False
```
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Redundant headers

Read https://github.com/psf/requests/blob/master/requests/models.py#L514 - your use of

    headers = {"content-type": "application/x-www-form-urlencoded"}

should not be needed.

Out-of-order progress

You print

    print(f"connecting to URL: {self.login_url}")

when this has already happened. Either say "connected to", or move this statement before your post.

Exception degradation

Resist the urge to sabotage the Python exception system. Rather than returning booleans from your methods, call response.raise_for_status, and optionally wrap that in a try/except/raise MyException() from e with your own exception type. raise_for_status will also obviate your status code checks.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for the feedback, I wasnt aware of raise for status, I have taken all your feedback and incorporated it into my code and testing strategy. \$\endgroup\$ Jan 1, 2021 at 20:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.