Search code examples
pythonunit-testingmockingpython-unittestpygithub

Python test patch is never called


I am trying to test with my code by mocking the PyGithub library. I want to create a repository for an organization. So first I need to get it and on the "Organization" returned object, I need to make another call.

It fails when trying to assert that my second method was called.

I am very new to python and I am guessing that there is a missing connection between the mocks but I cannot figure out what.


class GithubService:
    def __init__(self, token: str) -> None:
        self.__github__ = Github(token)
        self.__token__ = token

    def create_repo_extra(self, repo_name, description, organization_name, team_name):
        try:
            organization = self.__github__.get_organization(organization_name)
            repo = organization.create_repo(name=repo_name,
                                            description=description,
                                            private=True,
                                            has_issues=False,
                                            has_wiki=False,
                                            has_projects=False,
                                            allow_merge_commit=False)
            # do other things with the returned repo.....
            return True
        except GithubException as ex:
            print(ex.data)
            return False

Here is the test:

import unittest
from unittest.mock import patch, MagicMock, ANY

from github.Organization import Organization
from github.Repository import Repository

from src.github_service import GithubService


class TestGithubService(unittest.TestCase):

    @patch('github.Organization.Organization.create_repo',
           side_effect=MagicMock(return_value=Repository(ANY, {}, {}, True)))
    @patch('github.MainClass.Github.get_organization',
           return_value=MagicMock(return_value=Organization(ANY, {}, {}, True)))
    def test_create_repo_returns_true(self, get_organization, create_repo):

        sut = GithubService("token")
        actual = sut.create_repo_extra('repo-name', 'description', 'organization-name', 'team-name')

        get_organization.assert_called() # ok
        create_repo.assert_called()      # failed

        self.assertTrue(actual) 

Solution

  • Since you mock your Github.get_organization you can use the MagicMock it returns directly rather than trying to mock another layer.

    In this, I patch the same Github.get_organization, but avoid giving it a side effect or return value, and therefore pass it as an arg (like you did).

    Then I create a convenience mock_organization and it will be the return value of the patched Github.get_organization.

    Finally, the patch is checked like you did, and through the convenience mock_organization I check the create_repo method is called as well.

    class TestGithubService(unittest.TestCase):
    
        @patch("github.MainClass.Github.get_organization")
        def test_create_repo_returns_true(self, mock_get_organization):
    
            mock_organization = MagicMock()
            mock_get_organization.return_value = mock_organization
    
            sut = GithubService("token")
            actual = sut.create_repo_extra(
                "repo-name", "description", "organization-name", "team-name"
            )
    
            mock_get_organization.assert_called()  # ok
            mock_organization.create_repo.assert_called()  # ok
    
            self.assertTrue(actual)
    

    Without seeing more of your code I am not sure why patching Organization did not work, but this is simpler, cleaner and just as effective.