Як почати працювати в ІТ

Виконання завдань по роботі в ІТ галузі.


Project maintained by BobasB Hosted on GitHub Pages — Theme by mattgraham

Тестування

Це процес перевірки правильності роботи програм, їх функцій та поведінки при різних умовах роботи шляхом створення штучних ситуацій або сценаріїв.

  1. Для роботи нам буде потрібно створити декілька Python файлів.

Перевірка assert

  1. assert - це перевірка певних тверджень та встановлення працездатності коду. Твердження дозволяють перевірити правильність коду, перевіряючи, чи виконуються певні умови.
     number = -1
     assert number > 0, "число має бути більшим за нуль!"
    
  2. :star: Створіть власний крок assert та зробіть тестові превірки при введенні даних з клавіатури. Для цього використайте метод input як показано нижче:
     a = input("Введіть число: ")
     assert a.isdigit(), "Потрібно ввести число!"
     print(f"введене число: {a}")
    
  3. В ООП методи assert найкраще виконувати для перевірки (валідації) правильності вводу аргументів. Для прикладу маємо простий клас в якому здійснюємо валідацію даних перед тим як створювати обєкт. Умовою валідації може бути будь-який вираз Python який повертає значення True/False.
     class Figure:
         def __init__(self, type, length) -> None:
             assert length > 0, "Довжина має бути більшою за 0!"
             assert type in ["квадрат", "прямокутник", "трикутник"], "Дозволені фігури: квадрат, прямокутник, трикутник"
             self.type = type
             self.length = length
    
     #a = Figure("трапеція", 12)
     #b = Figure("квадрат", 0)
     c = Figure("квадрат", 1)
    
  4. :star: Виконайте код наведений вище для різних обєктів. Вставте у звіт результат виконання роботи для різної комбінації введених значень при створенні обєкта.
  5. Перевірки можна організовувати і іншими способами, наприклад з використанням умовного розгалуження. Якщо дані введені невірно, можна викликати клас помилки.
     class Name:
         def __init__(self, name) -> None:
             if name not in ["Богдан", "Анонім"]:
                 raise ValueError("Дозволені імена: Богдан, Анонім")
             self.name = name
    
     a = Name("Бодько")
    
  6. :star: Додайте власне імя в перевірку, та спробуйте створити такий обєкт. Додайте ще один аргумент в клас, наприклад хоббі, та здійсніть валідацію чи хоббі є введено (поле не пусте).

Юніт тести

Це перевірка малої частини коду, юніта. Найчастіше це порівняння між введеними даними та результатом виконання якоїсь частини програми.

  1. Найпростіше використовувати вбудовану бібліотеку для юніт тестів unittest. Зазвичай юніт тести створюються в окремому файлі, однак в межах проекту який має бути протестованим.
  2. Створимо простий клас з двома пропертями, та навмисно зробимо помилку в ньому. Для коректної роботи з тестами назвіть файл app.py. Це буди потрібно для імпорту бібліотек (класів).
     class Figure:
         FIGURES = ["квадрат", "прямокутник", "трикутник"]
         def __init__(self, type, length) -> None:
             assert length > 0, "Довжина має бути більшою за 0!"
             assert type in self.FIGURES, "Дозволені фігури: квадрат, прямокутник, трикутник"
             self.type = type
             self.length = length
    
         @property
         def get_figure_type(self):
             return self.type
    
         @property
         def get_figure_length(self):
             return self.type # робимо помилку
    
  3. :star: Спробуйте застосувати цей клас та створити декілька обєктів, викликати методи пропертіс.
  4. Створимо юніт тести та спробуємо перевірити тестовий клас щоб все працювало правильно. Для цьго у методі setUp з допомогою бібліотеки random створимо обєкт та перевіримо чи правильно працюють методи:
     import unittest
     from random import choice, randint
    
     from app import Figure # назва файлу з нашим класом повинна бути app.py
    
     class TestFigure(unittest.TestCase):
         @classmethod
         def setUpClass(cls):
             """Виконається лише раз на початку тестів
             """
             pass
            
         def setUp(self) -> None:
             """Виконується кожного разу коли запускається тест
             """
             self.figure = choice(Figure.FIGURES)
             self.length = randint(1, 10)
             self.obj = Figure(self.figure, self.length)
             return super().setUp()
    
         def tearDown(self) -> None:
             del self.obj
             return super().tearDown()
    
         def test_figure_type(self):
             print(f"Тестуємо вивід, має бути: {self.figure} == {self.obj.get_figure_type}")
             self.assertEqual(self.figure, self.obj.get_figure_type, "Властивість get_figure_type повертає непривильну фігуру!")
    
         def test_figure_lengh(self):
             self.assertEqual(self.length, self.obj.get_figure_length, "Властивість get_figure_length повертає непривильну довжину!")
            
         def test_obj(self):
             with self.assertRaises(AssertionError):
                 Figure("коло", 1) # Спробуємо створити обєкт з недозволеними параметрими, в нас має бути помилка AssertionError
    
    
     if __name__ == '__main__':
         unittest.main() # unittest.main(verbosity=2) щоб був більш детальний вивід
    
  5. Виконати тести можна двома способами:
    1. Якщо у програмі є блок з викликом бібліотеки unittest.main(), то програму можна викликати з Visual Studio Code через кнопку Run (трикутник :arrow_forward:) або який регулярний Python файл:
      python test.py
      
    2. Якщо викликати напряму з консолі (немає запуску unittest.main()), потрібно виконати команду з явним викликом бібліотеки unittest. Для більш детального виводу просто додайте опцію -v або --verbose:
      python -m unittest
      
  6. :star: Попрактикуйтесь викликати тести з консолі та з Visual Studio Code. Вкажіть які тести виконуються а які провалюються.
  7. :fire: При виконанні юніт тестів зявиться папка __pycache__ яку не варто комітити в репозиторій, тому додайте її в .gitignore.
  8. :star: Розширте функціонал класу який тестується та створіть юніт тест для перевірки правильності роботи доданого функціоналу.

Юніт тести з використання бібліотеки PyTest

PyTest це стороння бібліотета для тестування коду

  1. Більш докладно про бібліотеку PyTest можна прочитати на офіційному сайті. Оскільки, це стороння бібліотека її потрібно інсталювати. Для цього використаємо віртуальні середовища та інструмент pipenv або poetry (якщо pipenv буде некоректно працювати на Windows встановіть бібліотеку за допомогою pip). Бібліотеки для юніт тестів краще ставити в --dev середовище.
     pipenv --python 3.12
     pipenv install pytest --dev
     pipenv install --dev
     # або
     poetry init --name your_project --description "Your project description" --author "Your Name <your@email.com>"
     poetry add --dev pytest black flake8
     poetry install --with dev 
    
  2. Дана бібліотека може працювати як з вбудованими тестами які ми вже маємо, так і з будь-якими функціями які розпочинаються з слова test_. Для прикладу зробимо простий тест та помістимо всередині того ж файлу де і наш клас Figure:
    • створюємо простий тест у файл app.py:
       def test_app_triangle():
        """Test if we create triangle figure.
        """
        fig = "трикутник"
        triangle = Figure(fig, 4)
        assert triangle.type == fig, f"Фігура має бути {fig}"
      
    • запускаємо тести за допомогою pytest:
       poetry run pytest app.py
      
  3. :star: вкажіть у звіті що вивела програма та чи виконалась функція test_app_triangle.
  4. Для виклику всіх тестів з файлу test.py потібно передати даний файл як аргумент pytest:
     poetry run pytest test.py
    

Візуалізація результатів та покриття коду Coverage (pytest-cov)

Допоміжна бібліотека для збору статистика покриття коду юніт тестами. Покриття тестами - це відношення між кількістю рядків, виконаних хоча б одним тестом, до загальної кількості рядків кодової бази.

  1. Можна використовувати бібліотеку coverage або плагін pytest-cov.
    • для інсталяції потрібно виконати наступні команди:
       pipenv install coverage pytest-cov --dev
       # АБО
       poetry add --dev coverage pytest-cov
      
  2. Для виводу покриття коду тестами потрібно або використовувати новий інструмент coverage або передавати аргументи у виклик pytest.
    • стандартний запуск тестів:
       poetry run python -m unittest discover
       # АБО - якщо всі тести поміщені в окрему папку під назвою tests
       poetry run python -m unittest discover -s tests -v
       # АБО - використовуючи бібліотеку pytest
       poetry run pytest -v
      
    • виклик через coverage (у репозиторії має зявитись файл .coverage):
       poetry run python -m coverage report
       # АБО запуск того самого тестування unittest але вже з coverage
       poetry run coverage run -m unittest discover -s tests -v
       # АБО тестування з використанням бібліотеки pytest
       poetry run coverage run -m pytest
      
    • виклик через pytest. Тут ми передаємо параметр який саме модуль ми хочемо проаналізувати. В даному випадку наша бібліотека з класом знаходиться в файлі app.py тому ми передаємо параметр --cov=app:
       poetry run pytest --cov=app test.py
      
  3. Особливістю покриття є визначення “розгалужень при покритті”. Це можна продемонструвати на наступному прикладі. Додайте до нашого класу Figure наступний код:
     @property
     def get_angles(self):
         if self.type in ["квадрат", "прямокутник"]:
             return 4
         if self.type == "трикутник":
             return 3
    
    • а також створіть юніт-тест який протестує дану проперті:
       def test_get_angles():
        """Тестуємо чи правильно повертається кількість кутів фігури.
        """
        fig = "трикутник"
        triangle = Figure(fig, 1)
        assert triangle.get_angles == 3, f{fig} є 3 кути!"
      
    • перезапустіть визначення покриття та проаналізуйте отримані результати:
       poetry run python -m coverage report
      
  4. :star: Для візуалізації результаів не в консолі а через браузер - згенеруйте звіт у форматі html. Відкрийте файл index.html у веб-браузері та дослідіть отриману інформацію. Отриманий файл повинен бути у звіті.
     poetry run python -m coverage html
    
  5. :star: За замовчеванням звіт буде показувати всі файли проекту (навіть пусті, і навіть самі тести). Щоб обмежити звіт лише тими файлами які ви хочете, створіть файл .coveragerc з наступним вмістом:
     [run]
     source = app.py
    
     [report]
     omit =
         */__init__.py
         */tests/*
         */venv/*
    

    і знову виконайте генерацію звіту та проаналізуйте отримані результати:

     poetry run python -m coverage html
    

Автоматизація з GitHub Actions

виконання тестів можна автоматизувати що буде зроблено в наступній роботі

  1. Всі тести та їх виконання яке здійснювалось вручну у цій роботі буде потрібним для наступної.
  2. Автоматизація виконується щодо команд запуску тестів та результату їх роботи.

Здача роботи