├── pytest.ini ├── setup.cfg ├── requirements.txt ├── tests ├── conftest.py └── test_homework.py ├── .gitignore ├── homework.py └── README.md /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | norecursedirs = env/* 3 | addopts = -vv -p no:cacheprovider --disable-warnings 4 | testpaths = tests/ 5 | python_files = test_*.py 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | disable-noqa = True 3 | ignore = W503 4 | filename = 5 | ./homework.py 6 | max-complexity = 10 7 | max-line-length = 79 8 | exclude = 9 | tests 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==21.2.0 2 | flake8==4.0.1 3 | importlib-metadata==4.8.1 4 | iniconfig==1.1.1 5 | mccabe==0.6.1 6 | packaging==21.0 7 | pluggy==1.0.0 8 | py==1.10.0 9 | pycodestyle==2.8.0 10 | pyflakes==2.4.0 11 | pyparsing==2.4.7 12 | pytest==6.2.5 13 | pytest-tldr==0.2.4 14 | toml==0.10.2 15 | typing-extensions==3.10.0.2 16 | zipp==3.6.0 17 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | from io import StringIO 4 | 5 | BASE_DIR = Path(__file__).resolve(strict=True).parent.parent 6 | sys.path.append(str(BASE_DIR)) 7 | 8 | 9 | class Capturing(list): 10 | """ 11 | Class for capturing function stdout. 12 | Usage: 13 | with Capturing() as func_output: 14 | func() 15 | 16 | check func() output in func_output variable 17 | """ 18 | 19 | def __enter__(self): 20 | self._stdout = sys.stdout 21 | sys.stdout = self._stringio = StringIO() 22 | return self 23 | 24 | def __exit__(self, *args): 25 | self.extend(self._stringio.getvalue().splitlines()) 26 | del self._stringio 27 | sys.stdout = self._stdout 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Папки, создаваемые средой разработки 132 | .idea 133 | .DS_Store 134 | .AppleDouble 135 | .LSOverride 136 | 137 | *.sublime-project 138 | *.sublime-workspace 139 | 140 | .vscode/ 141 | *.code-workspace 142 | 143 | # Local History for Visual Studio Code 144 | .history/ 145 | 146 | .mypy_cache 147 | -------------------------------------------------------------------------------- /homework.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, asdict 2 | from typing import Dict, Type 3 | 4 | 5 | @dataclass 6 | class InfoMessage: 7 | """Информационное сообщение о тренировке.""" 8 | 9 | training_type: str 10 | duration: float 11 | distance: float 12 | speed: float 13 | calories: float 14 | 15 | INFORMATION = ('Тип тренировки: {training_type}; ' 16 | 'Длительность: {duration:.3f} ч.; ' 17 | 'Дистанция: {distance:.3f} км; ' 18 | 'Ср. скорость: {speed:.3f} км/ч; ' 19 | 'Потрачено ккал: {calories:.3f}.') 20 | 21 | def get_message(self) -> str: 22 | return (self.INFORMATION.format(**asdict(self))) 23 | 24 | 25 | class Training: 26 | """Базовый класс тренировки.""" 27 | 28 | LEN_STEP = 0.65 29 | M_IN_KM = 1000 30 | MIN_IN_H = 60 31 | 32 | def __init__(self, 33 | action: int, 34 | duration: float, 35 | weight: float, 36 | ) -> None: 37 | self.action = action 38 | self.duration = duration 39 | self.weight = weight 40 | 41 | def get_distance(self) -> float: 42 | """Получить дистанцию в км.""" 43 | return self.action * self.LEN_STEP / self.M_IN_KM 44 | 45 | def get_mean_speed(self) -> float: 46 | """Получить среднюю скорость движения.""" 47 | return self.get_distance() / self.duration 48 | 49 | def get_spent_calories(self) -> float: 50 | """Получить количество затраченных калорий.""" 51 | raise NotImplementedError( 52 | 'Определите get_spent_calories в %s.' % (self.__class__.__name__) 53 | ) 54 | 55 | def show_training_info(self) -> InfoMessage: 56 | """Вернуть информационное сообщение о выполненной тренировке.""" 57 | return InfoMessage(type(self).__name__, self.duration, 58 | self.get_distance(), self.get_mean_speed(), 59 | self.get_spent_calories(),) 60 | 61 | 62 | class Running(Training): 63 | """Тренировка: бег.""" 64 | 65 | COEFF_MID_SPEED = 18 66 | COEFF_MID_SPEED_1 = 20 67 | 68 | def get_spent_calories(self) -> float: 69 | return ((self.COEFF_MID_SPEED * self.get_mean_speed() 70 | - self.COEFF_MID_SPEED_1) 71 | * self.weight / self.M_IN_KM * self.duration * self.MIN_IN_H) 72 | 73 | 74 | class SportsWalking(Training): 75 | """Тренировка спортивная ходьба.""" 76 | 77 | COEFF_WEIGHT = 0.035 78 | COEFF_WEIGHT_1 = 0.029 79 | EXHIBITOR = 2 80 | 81 | def __init__(self, action: int, 82 | duration: float, 83 | weight: float, 84 | height: float) -> None: 85 | super().__init__(action, duration, weight) 86 | self.height = height 87 | 88 | def get_spent_calories(self) -> float: 89 | return ((self.COEFF_WEIGHT * self.weight + (self.get_mean_speed() 90 | ** self.EXHIBITOR // self.height) * self.COEFF_WEIGHT_1 91 | * self.weight) * self.duration * self.MIN_IN_H) 92 | 93 | 94 | class Swimming(Training): 95 | """Тренировка плавание.""" 96 | 97 | LEN_STEP = 1.38 98 | COEFF_MID_SPEED = 1.1 99 | COEFF_WEIGHT = 2 100 | 101 | def __init__(self, 102 | action: int, 103 | duration: float, 104 | weight: float, 105 | length_pool: int, 106 | count_pool: int) -> None: 107 | super().__init__(action, duration, weight) 108 | self.length_pool = length_pool 109 | self.count_pool = count_pool 110 | 111 | def get_spent_calories(self) -> float: 112 | return ((self.get_mean_speed() + self.COEFF_MID_SPEED) 113 | * self.COEFF_WEIGHT * self.weight) 114 | 115 | def get_mean_speed(self) -> float: 116 | return (self.length_pool * self.count_pool 117 | / self.M_IN_KM / self.duration) 118 | 119 | 120 | def read_package(workout_type: str, data: list) -> Training: 121 | """Прочитать данные полученные от датчиков.""" 122 | trainings: Dict[str, Type[Training]] = { 123 | 'SWM': Swimming, 124 | 'RUN': Running, 125 | 'WLK': SportsWalking 126 | } 127 | if workout_type not in trainings: 128 | raise KeyError(f'Тип тренировки не известен {workout_type}') 129 | return trainings[workout_type](*data) 130 | 131 | 132 | def main(training: Training) -> None: 133 | """Главная функция.""" 134 | print(training.show_training_info().get_message()) 135 | 136 | 137 | if __name__ == '__main__': 138 | packages = [ 139 | ('SWM', [720, 1, 80, 25, 40]), 140 | ('RUN', [15000, 1, 75]), 141 | ('WLK', [9000, 1, 75, 180]), 142 | ] 143 | 144 | for workout_type, data in packages: 145 | main(read_package(workout_type, data)) 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Модуль фитнес-трекера 2 | # Модуль расчета и отображения полной информации о тренировках по данным от блока датчиков. 3 | 4 | ## Задача 5 | Реализовать программный модуль по методологии ООП для расчета и отображения информации 6 | о прошедшей тренировке по данным от блока датчиков. 7 | 8 | ## Базовый класс 9 | ```python 10 | class Training 11 | ``` 12 | ### Свойства класса 13 | 14 | * action - основное считываемое действие во время тренировке (шаг - бег, ходьба; гребок - плавание); 15 | * duration - длительность тренировки; 16 | * weight - вес спортсмена; 17 | * M_IN_KM = 1000 - константа для перевода значений из метров в километры. Её значение — 1000. 18 | * LEN_STEP - расстояние, которое спортсмен преодалевает за один шаг или гребок. Один шаг — это `0.65` метра, один гребок 19 | при плавании — `1.38` метра. 20 | 21 | ### Методы класса 22 | 23 | * get_distance() - метод возвращает значение дистанции приодоленной за тренировку 24 | ```python 25 | # базовая формула расчета 26 | шаг * LEN_STEP / M_IN_KM 27 | ``` 28 | * get_mean_speed() - метод возвращает значение средней скорости движения во время тренировки 29 | ```python 30 | # базовая формула расчета 31 | дистанция / длительность 32 | ``` 33 | * get_spent_calories() - метод возвращает число потраченных колорий 34 | * show_training_info() - метод возвращает объект возвращает объект класса сообщения 35 | 36 | ## Классы наследнки 37 | Класс беговой тренировки 38 | ```python 39 | class Running 40 | ``` 41 | ### Свойства класса 42 | 43 | наследуюутся 44 | 45 | ### Методы класса 46 | пререопределить метод: 47 | * get_spent_calories() - метод возвращает число потраченных колорий 48 | ```python 49 | # формула расчета 50 | (18 * средняя_скорость - 20) * вес_спортсмена / M_IN_KM * время_тренировки_в_минутах 51 | ``` 52 | --- 53 | --- 54 | Класс спортивной ходьбы 55 | ```python 56 | class SportsWalking 57 | ``` 58 | ### Свойства класса 59 | Добавляемые свойства: 60 | * height - рост 61 | 62 | ### Методы класса 63 | пререопределить метод: 64 | * get_spent_calories() - метод возвращает число потраченных колорий 65 | ```python 66 | # формула расчета 67 | (18 * средняя_скорость - 20) * вес_спортсмена / M_IN_KM * время_тренировки_в_минутах 68 | ``` 69 | --- 70 | --- 71 | Класс тренировки в бассейне 72 | ```python 73 | class Swimming 74 | ``` 75 | ### Свойства класса 76 | Добавляемые свойства: 77 | * length_pool - длина бассейна 78 | * count_pool - количество проплытых бассейнов 79 | 80 | ### Методы класса 81 | пререопределить метод: 82 | * get_mean_speed() - метод возвращает значение средней скорости движения во время тренировки 83 | ```python 84 | # формула расчета 85 | длина_бассейна * count_pool / M_IN_KM / время_тренеровки 86 | ``` 87 | * get_spent_calories() - метод возвращает число потраченных колорий 88 | ```python 89 | # формула расчета 90 | (скорость + 1.1) * 2 * вес 91 | ``` 92 | ## Класс информационного сообщения 93 | ```python 94 | class InfoMessage 95 | ``` 96 | ### Свойства класса 97 | * training_type - тип тренировки 98 | * duration - длительность тренировки 99 | * distance -дистанция приодоленная за тренировку 100 | * speed - средняя скорость движения во время движения 101 | * calories - потраченные за время тренировки килокалории 102 | 103 | 104 | ### Методы класса 105 | 106 | * get_message() - метод выводит возвращает строку сообщения: 107 | ```python 108 | # выводимое сообщение 109 | # все значения типа float округляются до 3 знаков после запятой 110 | 'Тип тренировки: {training_type}; Длительность: {duration} ч.; Дистанция: {distance} км; Ср. скорость: {speed} км/ч; Потрачено ккал: {calories}'. 111 | ``` 112 | 113 | ## Функции модуля 114 | ```python 115 | def read_package 116 | ``` 117 | * Функция read_package принимает на вход код тренировки и список её параметров. 118 | * Функция должна определить тип тренировки и создать объект соответствующего класса, 119 | передав ему на вход параметры, полученные во втором аргументе. Этот объект функция должна вернуть. 120 | 121 | --- 122 | --- 123 | ```python 124 | def main(training) 125 | ``` 126 | Функция `main()` должна принимать на вход экземпляр класса `Training`. 127 | 128 | - При выполнении функции `main()`для этого экземпляра должен быть вызван метод `show_training_info()`; 129 | результатом выполнения метода должен быть объект класса `InfoMessage`, его нужно сохранить в переменную `info`. 130 | - Для объекта `InfoMessage`, сохранённого в переменной `info`, должен быть вызван метод, 131 | который вернет строку сообщения с данными о тренировке; эту строку нужно передать в функцию `print()`. 132 | 133 | ## Инструкции по установке 134 | ***- Клонируйте репозиторий:*** 135 | ``` 136 | git clone git@github.com:PashkaVRN/hw_python_oop.git 137 | ``` 138 | 139 | ***- Установите и активируйте виртуальное окружение:*** 140 | - для MacOS 141 | ``` 142 | python3 -m venv venv 143 | ``` 144 | - для Windows 145 | ``` 146 | python -m venv venv 147 | source venv/bin/activate 148 | source venv/Scripts/activate 149 | ``` 150 | 151 | ***- Установите зависимости из файла requirements.txt:*** 152 | ``` 153 | pip install -r requirements.txt 154 | -------------------------------------------------------------------------------- /tests/test_homework.py: -------------------------------------------------------------------------------- 1 | import re 2 | import pytest 3 | import types 4 | import inspect 5 | from conftest import Capturing 6 | 7 | try: 8 | import homework 9 | except ModuleNotFoundError: 10 | assert False, 'Не найден файл с домашней работой `homework.py`' 11 | except NameError as exc: 12 | name = re.findall("name '(\w+)' is not defined", str(exc))[0] 13 | assert False, f'Класс {name} не обнаружен в файле домашней работы.' 14 | except ImportError: 15 | assert False, 'Не найден файл с домашней работой `homework.py`' 16 | 17 | 18 | def test_read_package(): 19 | assert hasattr(homework, 'read_package'), ( 20 | 'Создайте функцию для обработки ' 21 | 'входящего пакета - `read_package`' 22 | ) 23 | assert callable(homework.read_package), ( 24 | 'Проверьте, что `read_package` - это функция.' 25 | ) 26 | assert isinstance(homework.read_package, types.FunctionType), ( 27 | 'Проверьте, что `read_package` - это функция.' 28 | ) 29 | 30 | 31 | @pytest.mark.parametrize('input_data, expected', [ 32 | (('SWM', [720, 1, 80, 25, 40]), 'Swimming'), 33 | (('RUN', [15000, 1, 75]), 'Running'), 34 | (('WLK', [9000, 1, 75, 180]), 'SportsWalking'), 35 | ]) 36 | def test_read_package_return(input_data, expected): 37 | result = homework.read_package(*input_data) 38 | assert result.__class__.__name__ == expected, ( 39 | 'Функция `read_package` должна возвращать класс ' 40 | 'вида спорта в зависимости от кода тренировки.' 41 | ) 42 | 43 | 44 | def test_InfoMessage(): 45 | assert inspect.isclass(homework.InfoMessage), ( 46 | 'Проверьте, что `InfoMessage` - это класс.' 47 | ) 48 | info_message = homework.InfoMessage 49 | info_message_signature = inspect.signature(info_message) 50 | info_message_signature_list = list(info_message_signature.parameters) 51 | for p in ['training_type', 'duration', 'distance', 'speed', 'calories']: 52 | assert p in info_message_signature_list, ( 53 | 'У метода `__init__` класса `InfoMessage` должен быть ' 54 | f'параметр {p}.' 55 | ) 56 | 57 | 58 | @pytest.mark.parametrize('input_data, expected', [ 59 | (['Swimming', 1, 75, 1, 80], 60 | 'Тип тренировки: Swimming; ' 61 | 'Длительность: 1.000 ч.; ' 62 | 'Дистанция: 75.000 км; ' 63 | 'Ср. скорость: 1.000 км/ч; ' 64 | 'Потрачено ккал: 80.000.' 65 | ), 66 | (['Running', 4, 20, 4, 20], 67 | 'Тип тренировки: Running; ' 68 | 'Длительность: 4.000 ч.; ' 69 | 'Дистанция: 20.000 км; ' 70 | 'Ср. скорость: 4.000 км/ч; ' 71 | 'Потрачено ккал: 20.000.' 72 | ), 73 | (['SportsWalking', 12, 6, 12, 6], 74 | 'Тип тренировки: SportsWalking; ' 75 | 'Длительность: 12.000 ч.; ' 76 | 'Дистанция: 6.000 км; ' 77 | 'Ср. скорость: 12.000 км/ч; ' 78 | 'Потрачено ккал: 6.000.' 79 | ), 80 | ]) 81 | def test_InfoMessage_get_message(input_data, expected): 82 | info_message = homework.InfoMessage(*input_data) 83 | assert hasattr(info_message, 'get_message'), ( 84 | 'Создайте метод `get_message` в классе `InfoMessage`.' 85 | ) 86 | assert callable(info_message.get_message), ( 87 | 'Проверьте, что `get_message` в классе `InfoMessage` - это метод.' 88 | ) 89 | result = info_message.get_message() 90 | assert isinstance(result, str), ( 91 | 'Метод `get_message` в классе `InfoMessage`' 92 | 'должен возвращать значение типа `str`' 93 | ) 94 | assert result == expected, ( 95 | 'Метод `get_message` класса `InfoMessage` должен возвращать строку.\n' 96 | 'Например: \n' 97 | 'Тип тренировки: Swimming; ' 98 | 'Длительность: 1.000 ч.; ' 99 | 'Дистанция: 75.000 км; ' 100 | 'Ср. скорость: 1.000 км/ч; ' 101 | 'Потрачено ккал: 80.000.' 102 | ) 103 | 104 | 105 | def test_Training(): 106 | assert inspect.isclass(homework.Training), ( 107 | 'Проверьте, что `Training` - это класс.' 108 | ) 109 | training = homework.Training 110 | training_signature = inspect.signature(training) 111 | training_signature_list = list(training_signature.parameters) 112 | for param in ['action', 'duration', 'weight']: 113 | assert param in training_signature_list, ( 114 | 'У метода `__init__` класса `Training` должен быть ' 115 | f' параметр {param}.' 116 | ) 117 | assert 'LEN_STEP' in list(training.__dict__), ( 118 | 'Задайте атрибут `LEN_STEP` в классе `Training`' 119 | ) 120 | assert training.LEN_STEP == 0.65, ( 121 | 'Длина шага в классе `Training` должна быть равна 0.65' 122 | ) 123 | assert 'M_IN_KM' in list(training.__dict__), ( 124 | 'Задайте атрибут `M_IN_KM` в классе `Training`' 125 | ) 126 | assert training.M_IN_KM == 1000, ( 127 | 'В классе `Training` укажите правильное ' 128 | 'количество метров в километре: 1000' 129 | ) 130 | 131 | 132 | @pytest.mark.parametrize('input_data, expected', [ 133 | ([9000, 1, 75], 5.85), 134 | ([420, 4, 20], 0.273), 135 | ([1206, 12, 6], 0.7838999999999999), 136 | ]) 137 | def test_Training_get_distance(input_data, expected): 138 | training = homework.Training(*input_data) 139 | assert hasattr(training, 'get_distance'), ( 140 | 'Создайте метод `get_distance` в классе `Training`.' 141 | ) 142 | result = training.get_distance() 143 | assert type(result) == float, ( 144 | 'Метод `get_distance` в классе `Trainig`' 145 | 'должен возвращать значение типа `float`' 146 | ) 147 | assert result == expected, ( 148 | 'Проверьте формулу подсчета дистанции класса `Training`' 149 | ) 150 | 151 | 152 | @pytest.mark.parametrize('input_data, expected', [ 153 | ([9000, 1, 75], 5.85), 154 | ([420, 4, 20], 0.06825), 155 | ([1206, 12, 6], 0.065325), 156 | ]) 157 | def test_Training_get_mean_speed(input_data, expected): 158 | training = homework.Training(*input_data) 159 | assert hasattr(training, 'get_mean_speed'), ( 160 | 'Создайте метод `get_mean_speed` в классе `Training`.' 161 | ) 162 | result = training.get_mean_speed() 163 | assert type(result) == float, ( 164 | 'Метод `get_mean_speed` в классе `Training`' 165 | 'должен возвращать значение типа `float`' 166 | ) 167 | assert result == expected, ( 168 | 'Проверьте формулу подсчёта средней скорости движения ' 169 | 'в классе `Training`' 170 | ) 171 | 172 | 173 | @pytest.mark.parametrize('input_data', [ 174 | ([9000, 1, 75]), 175 | ([420, 4, 20]), 176 | ([1206, 12, 6]), 177 | ]) 178 | def test_Training_get_spent_calories(input_data): 179 | training = homework.Training(*input_data) 180 | assert hasattr(training, 'get_spent_calories'), ( 181 | 'Создайте метод `get_spent_calories` в классе `Training`.' 182 | ) 183 | assert callable(training.get_spent_calories), ( 184 | 'Проверьте, что `get_spent_calories` - это функция.' 185 | ) 186 | 187 | 188 | def test_Training_show_training_info(monkeypatch): 189 | training = homework.Training(*[720, 1, 80]) 190 | assert hasattr(training, 'show_training_info'), ( 191 | 'Создайте метод `show_training_info` в классе `Training`.' 192 | ) 193 | 194 | def mock_get_spent_calories(): 195 | return 100 196 | monkeypatch.setattr( 197 | training, 198 | 'get_spent_calories', 199 | mock_get_spent_calories 200 | ) 201 | result = training.show_training_info() 202 | assert result.__class__.__name__ == 'InfoMessage', ( 203 | 'Метод `show_training_info` класса `Training` ' 204 | 'должен возвращать объект класса `InfoMessage`.' 205 | ) 206 | 207 | 208 | def test_Swimming(): 209 | assert hasattr(homework, 'Swimming'), 'Создайте класс `Swimming`' 210 | assert inspect.isclass(homework.Swimming), ( 211 | 'Проверьте, что `Swimming` - это класс.' 212 | ) 213 | assert issubclass(homework.Swimming, homework.Training), ( 214 | 'Класс `Swimming` должен наследоваться от класса `Training`.' 215 | ) 216 | swimming = homework.Swimming 217 | swimming_signature = inspect.signature(swimming) 218 | swimming_signature_list = list(swimming_signature.parameters) 219 | for param in ['action', 'duration', 'weight', 'length_pool', 'count_pool']: 220 | assert param in swimming_signature_list, ( 221 | 'У метода `__init__` класса `Swimming` ' 222 | f' должен быть параметр {param}.' 223 | ) 224 | assert 'LEN_STEP' in list(swimming.__dict__), ( 225 | 'Задайте атрибут `LEN_STEP` в классе `Swimming`' 226 | ) 227 | assert swimming.LEN_STEP == 1.38, ( 228 | 'Длина гребка в классе `Swimming` должна быть равна 1.38' 229 | ) 230 | 231 | 232 | @pytest.mark.parametrize('input_data, expected', [ 233 | ([720, 1, 80, 25, 40], 1.0), 234 | ([420, 4, 20, 42, 4], 0.042), 235 | ([1206, 12, 6, 12, 6], 0.005999999999999999), 236 | ]) 237 | def test_Swimming_get_mean(input_data, expected): 238 | swimming = homework.Swimming(*input_data) 239 | result = swimming.get_mean_speed() 240 | assert result == expected, ( 241 | 'Переопределите метод `get_mean_speed` в классе `Swimming`. ' 242 | 'Проверьте формулу подсчёта средней скорости в классе `Swimming`' 243 | ) 244 | 245 | 246 | @pytest.mark.parametrize('input_data, expected', [ 247 | ([720, 1, 80, 25, 40], 336.0), 248 | ([420, 4, 20, 42, 4], 45.68000000000001), 249 | ([1206, 12, 6, 12, 6], 13.272000000000002), 250 | ]) 251 | def test_Swimming_get_spent_calories(input_data, expected): 252 | swimming = homework.Swimming(*input_data) 253 | result = swimming.get_spent_calories() 254 | assert type(result) == float, ( 255 | 'Переопределите метод `get_spent_calories` в классе `Swimming`.' 256 | ) 257 | assert result == expected, ( 258 | 'Проверьте формулу расчёта потраченных калорий в классе `Swimming`' 259 | ) 260 | 261 | 262 | def test_SportsWalking(): 263 | assert hasattr(homework, 'SportsWalking'), 'Создайте класс `SportsWalking`' 264 | assert inspect.isclass(homework.SportsWalking), ( 265 | 'Проверьте, что `SportsWalking` - это класс.' 266 | ) 267 | assert issubclass(homework.SportsWalking, homework.Training), ( 268 | 'Класс `SportsWalking` должен наследоваться от класса `Training`.' 269 | ) 270 | sports_walking = homework.SportsWalking 271 | sports_walking_signature = inspect.signature(sports_walking) 272 | sports_walking_signature_list = list(sports_walking_signature.parameters) 273 | for param in ['action', 'duration', 'weight', 'height']: 274 | assert param in sports_walking_signature_list, ( 275 | 'У метода `__init__` класса `SportsWalking` ' 276 | f'должен быть параметр {param}.' 277 | ) 278 | 279 | 280 | @pytest.mark.parametrize('input_data, expected', [ 281 | ([9000, 1, 75, 180], 157.50000000000003), 282 | ([420, 4, 20, 42], 168.00000000000003), 283 | ([1206, 12, 6, 12], 151.20000000000002), 284 | ]) 285 | def test_SportsWalking_get_spent_calories(input_data, expected): 286 | sports_walking = homework.SportsWalking(*input_data) 287 | result = sports_walking.get_spent_calories() 288 | assert type(result) == float, ( 289 | 'Переопределите метод `get_spent_calories` в классе `SportsWalking`.' 290 | ) 291 | assert result == expected, ( 292 | 'Проверьте формулу подсчёта потраченных ' 293 | 'калорий в классе `SportsWalking`' 294 | ) 295 | 296 | 297 | def test_Running(): 298 | assert hasattr(homework, 'Running'), 'Создайте класс `Running`' 299 | assert inspect.isclass(homework.Running), ( 300 | 'Проверьте, что `Running` - это класс.' 301 | ) 302 | assert issubclass(homework.Running, homework.Training), ( 303 | 'Класс `Running` должен наследоваться от класса `Training`.' 304 | ) 305 | 306 | 307 | @pytest.mark.parametrize('input_data, expected', [ 308 | ([9000, 1, 75], 383.85), 309 | ([420, 4, 20], -90.1032), 310 | ([1206, 12, 6], -81.32032799999999), 311 | ]) 312 | def test_Running_get_spent_calories(input_data, expected): 313 | running = homework.Running(*input_data) 314 | assert hasattr(running, 'get_spent_calories'), ( 315 | 'Создайте метод `get_spent_calories` в классе `Running`.' 316 | ) 317 | result = running.get_spent_calories() 318 | assert type(result) == float, ( 319 | 'Переопределите метод `get_spent_calories` в классе `Running`.' 320 | ) 321 | assert result == expected, ( 322 | 'Проверьте формулу расчёта потраченных калорий в классе `Running`' 323 | ) 324 | 325 | 326 | def test_main(): 327 | assert hasattr(homework, 'main'), ( 328 | 'Создайте главную функцию программы с именем `main`.' 329 | ) 330 | assert callable(homework.main), 'Проверьте, что `main` - это функция.' 331 | assert isinstance(homework.main, types.FunctionType), ( 332 | 'Проверьте, что `main` - это функция.' 333 | ) 334 | 335 | 336 | @pytest.mark.parametrize('input_data, expected', [ 337 | (['SWM', [720, 1, 80, 25, 40]], [ 338 | 'Тип тренировки: Swimming; ' 339 | 'Длительность: 1.000 ч.; ' 340 | 'Дистанция: 0.994 км; ' 341 | 'Ср. скорость: 1.000 км/ч; ' 342 | 'Потрачено ккал: 336.000.' 343 | ]), 344 | (['RUN', [1206, 12, 6]], [ 345 | 'Тип тренировки: Running; ' 346 | 'Длительность: 12.000 ч.; ' 347 | 'Дистанция: 0.784 км; ' 348 | 'Ср. скорость: 0.065 км/ч; ' 349 | 'Потрачено ккал: -81.320.' 350 | ]), 351 | (['WLK', [9000, 1, 75, 180]], [ 352 | 'Тип тренировки: SportsWalking; ' 353 | 'Длительность: 1.000 ч.; ' 354 | 'Дистанция: 5.850 км; ' 355 | 'Ср. скорость: 5.850 км/ч; ' 356 | 'Потрачено ккал: 157.500.' 357 | ]) 358 | ]) 359 | def test_main_output(input_data, expected): 360 | with Capturing() as get_message_output: 361 | training = homework.read_package(*input_data) 362 | homework.main(training) 363 | assert get_message_output == expected, ( 364 | 'Метод `main` должен печатать результат в консоль.\n' 365 | ) 366 | --------------------------------------------------------------------------------