Множественное наследование Python работает только для метода одного из родителей, но вызывает метод от обоих родителей - PullRequest
0 голосов
/ 22 октября 2019

У меня вопрос Python / OOP.
Вы все знакомы с проблемой алмазов в C++ верно? Это что-то похожее.
У меня есть следующие классы

class BaseAuth(TestCase):

    def setUp(self):
        # create dummy user and client, removing code of that for simplicity.
        self.user.save()
        self.client.save()

    def _get_authenticated_api_client(self):
        pass

class TokenAuthTests(BaseAuth):

    def _get_authenticated_api_client(self):
        super()._get_authenticated_api_client()
        print("I AM IN TOKEN AUTH")
        api_client = APIClient()
        # do stuff with api_client
        return api_client

class BasicAuthTests(BaseAuth):

    def _get_authenticated_api_client(self):
        super()._get_authenticated_api_client()
        print("I AM IN BASIC AUTH")
        api_client = APIClient()
        # do stuff with api client
        return api_client

class ClientTestCase(BasicAuthTests, TokenAuthTests):

    def test_get_login_response(self):
        api_client = self._get_authenticated_api_client()
        # login test code

    def test_get_clients(self):
        api_client = self._get_authenticated_api_client()
        # get client test code

    def test_get_client_by_id(self):
        api_client = self._get_authenticated_api_client()
        # get client by id test code

    def test_update_client_by_id(self):
        api_client = self._get_authenticated_api_client()
        # update client test code

    def test_add_client(self):
        api_client = self._get_authenticated_api_client()
        # add client test code

    def test_delete_client_by_id(self):
        api_client = self._get_authenticated_api_client()
        # delete client test code

Теперь, когда я запускаю код, я вижу, что это распечатано:

I AM IN TOKEN AUTH
I AM IN BASIC AUTH
.I AM IN TOKEN AUTH
I AM IN BASIC AUTH
.I AM IN TOKEN AUTH
I AM IN BASIC AUTH
.I AM IN TOKEN AUTH
I AM IN BASIC AUTH
.I AM IN TOKEN AUTH
I AM IN BASIC AUTH
.I AM IN TOKEN AUTH
I AM IN BASIC AUTH

Но еслиЯ вижу функциональность, тесты выполняются только для BasicAuthTests. Откуда я знаю это?

1. Количество тестовых прогонов равно 6, когда оно должно быть 12, 6 для каждого родительского класса.

2. Если яизмените функцию _get_authenticated_api_client() в BasicAuthTests на неправильный тип return, код вылетает, но если я изменю его в TokenAuthTests, ничего не произойдет, что означает, что TokenAuthTests ничего не делает, но его оператор print в работечто означает, что функция вызывается.

Это так запутанно, может кто-нибудь помочь?
Моя конечная цель - запустить эти 6 тестов для каждого из возвращенных родительских классов api_client.

ЗДЕСЬ ДОЛЖЕН ЗАМЕТИТЬ, ЧТО КОД ОТКРЫЛ. И ТЕСТ РАБОТАЕТ ВСЕГДА, ЕСЛИ Я ИСПОЛЬЗУЮ ИХ ОТДЕЛЬНО. Я пытаюсь упростить код и удалить все повторения, поэтому я хочу попробовать слить их в один файл.

Ответы [ 2 ]

1 голос
/ 22 октября 2019

Вы застряли с нашим MRO . Хотя Python поддерживает Diamond Inheritance , вы застреваете, когда дело доходит до назначения переменных.

Благодаря Diamond Inheritance , теперь мы оба _get_authenticated_api_client будем называть. Когда дело доходит до назначения, то, что возвращает _get_authenticated_api_client, зависит от того, как вы проходите ваши классы.

В вашем случае, только 6 тестов было выполнено, потому что у вас есть только один класс с 6 функциями тестов.

Вы намереваетесь запустить точно такие же тесты с разными auth методами. Один простой способ сделать это:

class ClientMixinCase: #Do not Inherit from "TestCase"
    #... Declare you tests here

class ClientWithTokenAuthTestCase(ClientMixinCase,TokenAuthTests):
    pass

class ClientWithBasicAuthTestCase(ClientMixinCase,BasicAuthTests):
    pass

0 голосов
/ 22 октября 2019

Таким образом, я решил эту проблему, используя другой подход.

Мы можем объявить все тесты в родительском классе и объявить функцию _get_authenticated_api_client в дочерних классах.

Но как мы можем использовать функцию дочернего класса в родительском классе?

Когда родительский класс наследует TestCase и будет первым классом, из которого будут выполняться тесты, и он потерпит крах, потому что у него не будет функции _get_authenticated_api_client.

Чтобы избежать этого, чтомы делаем, мы заключаем родительский класс в другой класс, таким образом, этот класс будет рассматриваться не как класс, а как объект родительского класса.

Но когда мы наследуем наши дочерние классы от этого свернутого родителя TestCase унаследованный класс, он будет обрабатываться как класс, и его тесты будут выполняться, но в этот момент функция _get_authenticated_api_client будет объявлена ​​в child. Вот код

class WrapperTestClass:

    class ClientTestCase(TestCase):

        # all the tests and setup function

class TokenAuthTests(WrapperTestClass.ClientTestCase):

    def get_authenticated_api_client(self):
        # function that returns api_client

class BasicAuthTests(WrapperTestClass.ClientTestCase):

    def get_authenticated_api_client(self):
        # function that returns api_client
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...