Метод dataclasses.asdict каким-либо образом изменяет фиктивный объект

У меня странная проблема с классами данных Python, когда они связаны с фиктивными объектами. Ниже приведена моя база кода, и она организована в этой структуре каталогов:

.
├── setup.py
├── src
│   └── sample
│       ├── app.py
│       ├── configuration.py
│       ├── __init__.py
│       └── resource.py
└── tests
    └── test_sample.py

Ниже приведен код для resource.py:

import flask_restful
import flask
import datetime


class Person:
    def __init__(self, name, year):
        self.name = name
        self.birthyear = year

    def age_of_person(self):
        today = datetime.datetime.now()
        return today.year - self.birthyear


class SampleResource(flask_restful.Resource):
    def __init__(self, person):
        self.person = person

    def get(self):
        age = self.person.age_of_person()
        return flask.jsonify({"name": self.person.name, "age": age})

Итак, у нас есть одна пустышка Person класс и один SampleResource(ресурс flask_restful) . Ресурс принимает экземпляр человека, а затем использует свои методы внутри своегоget метод.

Ниже приведен код моего configuration.py:

import dataclasses
import sample.resource
from typing import NamedTuple


@dataclasses.dataclass()
class ResourceClsKwargs():
    person: sample.resource.Person

    # def asdict(self):
    #   output = dict()
    #   for field in dataclasses.fields(self):
    #       output[field.name] = getattr(self, field.name)
    #   return output

# class ResourceClsKwargs(NamedTuple):
#   person: sample.resource.Person

Итак, это простой класс данных, в котором resource_class_kwargs который требуется передать ресурсу flask_restful

В app.py составляет:

import sample.resource
import sample.configuration
import dataclasses
import flask
import flask_restful


def main(person):
    resource_cls_kwargs = dataclasses.asdict(
        sample.configuration.ResourceClsKwargs(person=person)
    )
    # resource_cls_kwargs = sample.configuration.ResourceClsKwargs(person=person)._asdict()
    # resource_cls_kwargs = sample.configuration.ResourceClsKwargs(person=person).asdict()
    app = flask.Flask(__name__)
    api = flask_restful.Api(app)
    api.add_resource(
        sample.resource.SampleResource,
        "/sample",
        resource_class_kwargs=resource_cls_kwargs,
    )
    return app


if __name__ == "__main__":
    subhayan = sample.resource.Person(name="Subhayan", year=1984)
    app = main(subhayan)
    app.run(debug=True)

Таким образом, создаются соответствующие объекты и просто запускается приложение.

Наконец, это код тестового файла:

"""Test file for project."""

import flask
import sample.app
import sample.resource


def test_flask_app_is_returned_from_main():
    shaayan = sample.resource.Person(name="Shaayan", year=1988)
    app = sample.app.main(shaayan)

    assert isinstance(app, flask.Flask)


def test_that_age_of_person_method_is_run(mocker):
    mock = mocker.Mock()
    mock.age_of_person.return_value = 36
    mock.name = "Subhayan"
    expected_ouput = {
        "name":"Subhayan",
        "age": 36
    }
    app = sample.app.main(mock)
    assert isinstance(app, flask.Flask)

    app.config['TESTING'] = True

    with app.test_client() as client:
        response = client.get('/sample')

    assert response.status_code == 200
    assert response.json == expected_ouput
    mock.age_of_person.assert_called()

Итак, когда я запускаю это, все утверждения проходят, кроме последнего. Таким образом, даже несмотря на то, что методы фиктивных объектов были вызваны, они все равно не работают.assert_called.

mocker = <pytest_mock.plugin.MockFixture object at 0x7f2761248f10>

    def test_that_age_of_person_method_is_run(mocker):
        mock = mocker.Mock()
        mock.age_of_person.return_value = 36
        mock.name = "Subhayan"
        expected_ouput = {
            "name":"Subhayan",
            "age": 36
        }
        app = sample.app.main(mock)
        assert isinstance(app, flask.Flask)
    
        app.config['TESTING'] = True
    
        with app.test_client() as client:
            response = client.get('/sample')
    
        assert response.status_code == 200
        assert response.json == expected_ouput
>       mock.age_of_person.assert_called()
E    AssertionError: Expected 'age_of_person' to have been called.

tests/test_sample.py:33: AssertionError
================================================================== short test summary info ===================================================================
FAILED tests/test_sample.py::test_that_age_of_person_method_is_run - AssertionError: Expected 'age_of_person' to have been called.

Как ни странно, если я изменил класс данных на именованный набор, все тесты проходят так:

Код для configuration.py:

import dataclasses
import sample.resource
from typing import NamedTuple


# @dataclasses.dataclass()
# class ResourceClsKwargs():
#     person: sample.resource.Person

    # def asdict(self):
    #   output = dict()
    #   for field in dataclasses.fields(self):
    #       output[field.name] = getattr(self, field.name)
    #   return output

class ResourceClsKwargs(NamedTuple):
    person: sample.resource.Person

Код для app.py:

import sample.resource
import sample.configuration
import dataclasses
import flask
import flask_restful


def main(person):
    # resource_cls_kwargs = dataclasses.asdict(
    #     sample.configuration.ResourceClsKwargs(person=person)
    # )
    resource_cls_kwargs = sample.configuration.ResourceClsKwargs(person=person)._asdict()
    # resource_cls_kwargs = sample.configuration.ResourceClsKwargs(person=person).asdict()
    app = flask.Flask(__name__)
    api = flask_restful.Api(app)
    api.add_resource(
        sample.resource.SampleResource,
        "/sample",
        resource_class_kwargs=resource_cls_kwargs,
    )
    return app


if __name__ == "__main__":
    subhayan = sample.resource.Person(name="Subhayan", year=1984)
    app = main(subhayan)
    app.run(debug=True)

Я не уверен, есть ли какое-то поведение в отношении классов данных, о котором я должен знать.

Большое спасибо за вашу помощь заранее.

0 ответов

Другие вопросы по тегам