Image

Автоматизированное тестирование: концепция разработки программного обеспечения, которую специалисты по данным должны знать для достижения успеха

Содержание

Как тестировать свой код и почему это важно в науке о данных и вашей карьере

Делиться

8f0cc4cbecf9acfde7458f46860f8265

Почему вам стоит прочитать эту статью

Большинство специалистов по обработке данных создают Jupyter Notebook, экспериментируют с несколькими ячейками, а затем поддерживают все процессы обработки данных и обучения моделей в том же блокноте.

Код тестируется один раз, когда блокнот был впервые написан, а затем он игнорируется в течение неопределенного периода времени — дней, недель, месяцев, лет, пока:

  • Необходимо перезапустить выходные данные блокнота, чтобы заново сгенерировать потерянные данные.
  • Для повторного обучения модели необходимо перезапустить блокнот с другими параметрами.
  • Что-то нужно было изменить на предыдущем этапе, и блокнот необходимо перезапустить, чтобы обновить наборы данных на последующих этапах.

У многих из вас, читая это, пробежали мурашки по спине…

Почему?

Потому что вы инстинктивно понимаете, что этот ноутбук никогда не запустится.

Вы же понимаете, что в лучшем случае код в этом блокноте придется отлаживать часами, а в худшем — переписывать с нуля.

В обоих случаях вам понадобится много времени, чтобы получить то, что вам нужно.

Почему это происходит?

Есть ли способ избежать этого?

Существует ли лучший способ написания и поддержки кода?

На этот вопрос мы и постараемся ответить в этой статье.

Решение: автоматизированное тестирование

Что это такое?

Как следует из названия, автоматизированное тестирование — это процесс запуска предопределенного набора тестов для вашего кода, чтобы убедиться, что он работает так, как ожидается.

Эти тесты проверяют, что ваш код работает ожидаемым образом, особенно после изменений или дополнений, и предупреждают вас о любых сбоях. Это избавляет от необходимости вручную тестировать код и запускать его на реальных данных.

Удобно, не правда ли?

Типы автоматизированного тестирования

Существует так много различных типов тестирования, что рассмотрение их всех выходит за рамки этой статьи.

Давайте сосредоточимся на двух основных типах, наиболее важных для специалиста по анализу данных:

  • Модульные тесты
  • Интеграционные тесты

Модульные тесты

7c816ead817031da910974e789ea81c1

Тестирует мельчайшие части кода изолированно (например, функцию).

Для удобства тестирования функция должна выполнять только одно действие: подать ей известные входные данные и проверить, соответствует ли результат ожидаемому.

Интеграционные тесты

a78499b8318f6cbc36c921ebef71af15

Проверяет, как работают вместе несколько компонентов.

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

Практический пример

Хватит теории, давайте посмотрим, как это работает на практике.

Мы рассмотрим простой пример, в котором специалист по работе с данными написал некоторый код в блокноте Jupyter (или скрипт), который многие специалисты по работе с данными видели в своей работе.

Мы выясним, почему код плох. Затем постараемся его улучшить.

Под «лучше» мы подразумеваем:

  • Легко проверить
  • Легко читать

что в конечном счете означает простоту поддержки, поскольку в долгосрочной перспективе хороший код — это код, который работает, продолжает работать и который легко поддерживать.

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

Затем мы рассмотрим некоторые практические правила относительно того, какой код следует тестировать.

Наконец, мы рассмотрим, как проводить тесты и как структурировать проекты.

2a11783faa978e44f1f9dbc2915ffa57

Пример трубопровода

В качестве примера мы будем использовать следующий конвейер:

# bad_pipeline.py import pandas as pd # Загрузка данных df1 = pd.read_csv(«data/users.csv») df2 = pd.read_parquet(«data/transactions.parquet») df3 = pd.read_parquet(«data/products.parquet») # Предварительная обработка # Объединение данных пользователя и транзакции df = df2.merge(df1, how='left', on='user_id') # Объединение с данными о продукте df = df.merge(df3, how='left', on='product_id') # Фильтрация по последним транзакциям df = df[df['transaction_date'] > '2023-01-01'] # Расчет общей цены df['total_price'] = df['quantity'] * df['price'] # Создание сегмента клиентов df['segment'] = df['total_price'].apply(lambda x: 'high' if x > 100 else 'low') # Удалить ненужные столбцы df = df.drop(['user_email', 'product_description', 'price'], axis=1) # Группировать по пользователю и сегменту, чтобы получить общую потраченную сумму df = df.groupby(['user_id', 'segment']).agg({'total_price': 'sum'}).reset_index() # Сохранять вывод df.to_parquet(«data/final_output.parquet»)

В реальной жизни мы бы увидели сотни строк кода, забитых в один блокнот. Но этот скрипт — яркий пример всего того, что нужно исправить в типичных блокнотах для работы с данными.

Этот код делает следующее:

  1. Загружает данные о пользователях, транзакциях и продуктах.
  2. Объединяет их в единый набор данных.
  3. Фильтрует недавние транзакции.
  4. Добавляет вычисляемые поля (total_price, segment).
  5. Удаляет ненужные столбцы.
  6. Объединяет общие расходы на пользователя и сегмент.
  7. Сохраняет результат как файл Parquet.

Почему этот трубопровод плохой?

О, есть множество причин, по которым такой подход к кодированию плох, в зависимости от того, с какой стороны на это посмотреть. Проблема не в содержании, а в его структуре.

Хотя недостатки такого способа написания кода можно обсуждать с разных точек зрения, в этой статье мы сосредоточимся на тестируемости.

1. Тесно связанная логика (другими словами, отсутствие модульности)

Все операции сжаты в один скрипт и выполняются одновременно. Неясно, что делает каждая часть, пока не прочитаешь каждую строку. Даже для такого простого скрипта это сложно. В реальных скриптах ситуация может только усугубиться, когда код достигает сотен строк.

Это делает невозможным проведение тестирования.

Единственный способ сделать это — запустить все сразу от начала до конца, вероятно, на реальных данных, которые вы собираетесь использовать.

Если ваш набор данных небольшой, то, возможно, вам удастся обойтись без этого. Но в большинстве случаев специалисты по анализу данных работают с огромными массивами данных, поэтому быстро провести какой-либо тест или проверку работоспособности не представляется возможным.

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

2. Отсутствие параметризации

Жёстко заданные пути к файлам и значения, например, 2023-01-01, делают код хрупким и негибким. Опять же, сложно тестировать что-либо, кроме реальных/производственных данных.

Нет никакой гибкости в том, как мы можем запустить код, все фиксировано.

Что еще хуже, как только вы что-то меняете, у вас нет никаких гарантий, что дальше по сценарию ничего не сломается.

Например, сколько раз вы вносили изменения, которые считали неопасными, а потом, запустив код, обнаруживали, что совершенно неожиданная его часть дает сбой?

Как улучшить?

Теперь давайте шаг за шагом рассмотрим, как можно улучшить этот код.

Обратите внимание: в дальнейшем мы будем предполагать, что для наших тестов мы будем использовать модуль pytest.

1. Понятная, настраиваемая точка входа

def run_pipeline( user_path: str, transaction_path: str, product_path: str, output_path: str, cutoff_date: str = '2023-01-01' ): # Загрузка данных … # Обработка данных … # Сохранить результат …

Начнем с создания одной функции, которую можно запускать из любого места, с понятными аргументами, которые можно изменять.

Чего это даёт?

Это позволяет нам эксплуатировать трубопровод в особых условиях испытаний.

# ДАНЫ НЕКОТОРЫЕ ТЕСТОВЫЕ ДАННЫЕ test_args = dict( test_user_path = «/fake_users.csv», test_transaction_path = «/fake_transaction.parquet», test_product_path = «/fake_products.parquet», test_cutoff_date = ««, ) # ЗАПУСКАЕМ КОНВЕЙЕР, КОТОРЫЙ ДОЛЖЕН БЫТЬ ПРОВЕРЕН run_pipeline(**test_args) # ПРОВЕРЯЕМ, СООТВЕТСТВУЕТ ЛИ ВЫХОД ОЖИДАЕМЫМ output = expected_output = assert output == expected_output

Вы можете сразу же начать передавать различные входные данные, различные параметры в зависимости от граничного случая, который вы хотите проверить.

Это дает вам гибкость в запуске кода в различных настройках, упрощая управление входными и выходными данными вашего кода.

Такое написание конвейера открывает путь к его интеграционному тестированию. Подробнее об этом в следующей статье.

2. Группируйте код в осмысленные фрагменты, которые выполняют одну функцию и выполняют ее хорошо.

Вот здесь-то и появляется немного искусства: разные люди организуют код по-разному, в зависимости от того, какие его части они считают важными.

Не существует правильного или неправильного ответа, но здравый смысл подсказывает, что функция должна выполнять одну задачу и делать это хорошо. Сделайте это, и тестировать её станет легко.

Один из способов группировки нашего кода выглядит следующим образом:

def load_data(user_path: str, transaction_path: str, product_path: str): «»»Загрузка данных из указанных путей»»» df1 = pd.read_csv(user_path) df2 = pd.read_parquet(transaction_path) df3 = pd.read_parquet(product_path) return df1, df2, df3 def create_user_product_transaction_dataset(user_df:pd.DataFrame, transaction_df:pd.DataFrame, product_df:pd.DataFrame ): «»»Объединяет данные о пользователях, транзакциях и продуктах в один набор данных. Набор данных определяет, какой пользователь купил продукт, в какое время и по какой цене. Аргументы: user_df (pd.DataFrame): Фрейм данных, содержащий информацию о пользователе. Должен иметь столбец 'user_id', который однозначно идентифицирует каждого пользователя. transaction_df (pd.DataFrame): Фрейм данных, содержащий информацию о транзакциях. Должен содержать столбцы «user_id» и «product_id», которые являются внешними ключами к фреймам данных пользователя и продукта соответственно. product_df (pd.DataFrame): Фрейм данных, содержащий информацию о продукте. Должен содержать столбец «product_id», который уникально идентифицирует каждый продукт. Возвращает: Фрейм данных, объединяющий данные о пользователе, транзакции и продукте в один набор данных. «»» df = transaction_df.merge(user_df, how='left', on='user_id') df = df.merge(product_df, how='left', on='product_id') return df def drop_unnecessary_date_period(df:pd.DataFrame, cutoff_date: str): «»»Удаляет транзакции, произошедшие до даты окончания. Примечание: Всё, что произошло до даты окончания, может быть удалено по <причинам>. Аргументы: df (pd.DataFrame): Таблица данных со столбцом `transaction_date` cutoff_date (str): Дата в формате `yyyy-MM-dd` Возвращает: Таблица данных с транзакциями, которые произошли после даты отсечения «»» df = df[df['transaction_date'] > cutoff_date] return df def compute_secondary_features(df:pd.DataFrame) -> pd.DataFrame: «»»Вычисляет вторичные признаки. Аргументы: df (pd.DataFrame): Таблица данных со столбцами `quantity` и `price` Возвращает: Таблица данных с добавленными к ней столбцами `total_price` и `segment`. «»» df['total_price'] = df['quantity'] * df['price'] df['segment'] = df['total_price'].apply(лямбда x: 'high' если x > 100 иначе 'low') вернуть df

Чего достигает группировка?

Лучшая документация

Ну, во-первых, в вашем коде появляется естественное пространство для добавления строк документации. Почему это важно? А вы пробовали читать свой код через месяц после написания?

Люди очень быстро забывают детали, и даже код, который вы написали, может стать неразборчивым всего за несколько дней.

Крайне важно документировать, что делает код, какие входные данные он ожидает получить и что он возвращает.

Включение строк документации в код обеспечивает контекст и задает ожидания относительно того, как должна себя вести функция, что упрощает понимание и отладку невыполненных тестов в будущем.

Лучшая читаемость

«Инкапсулируя» сложность вашего кода в более мелкие функции, вы можете облегчить чтение и понимание общего потока конвейера без необходимости читать каждую отдельную строку кода.

def run_pipeline( user_path: str, transaction_path: str, product_path: str, output_path: str, cutoff_date: str ): user_df, transaction_df, product_df = load_data( user_path, transaction_path, product_path ) df = create_user_product_transaction_dataset( user_df, transaction_df, product_df ) df = drop_unnecessary_date_period(df, cutoff_date) df = compute_secondary_features(df) df.to_parquet(output_path)

Вы предоставили читателю иерархию информации, и она дает читателю пошаговую разбивку того, что происходит в функции run_pipeline, с помощью осмысленных имен функций.

Затем читатель может по своему усмотрению просмотреть определение функции и ее сложность в зависимости от своих потребностей.

Процесс объединения кода в «осмысленные» фрагменты, подобные этому, демонстрирует концепцию, называемую «инкапсуляцией» и «абстракцией».

Более подробную информацию об инкапсуляции вы можете прочитать в моей статье на эту тему здесь.

Меньшие пакеты кода для тестирования

Далее, у нас есть очень конкретный, чётко определённый набор функций, которые выполняют одну задачу. Это упрощает тестирование и отладку, поскольку нам нужно заботиться только об одном.

Ниже вы можете узнать, как мы строим тест.

Создание модульного теста

1. Следуйте шаблону AAA

def test_create_user_product_transaction_dataset(): # ДАННО # ЗАПУСК # ТЕСТ …

Во-первых, мы определяем тестовую функцию, названную соответственно test_<имя_функции>.

Затем мы разделим его на три части:

  • ДАНЫ: входные данные функции и ожидаемый результат. Настройте всё необходимое для запуска функции, которую мы хотим протестировать.
  • RUN: запустить функцию с учетом входных данных.
  • ТЕСТ: сравните вывод функции с ожидаемым выводом.

Это общий шаблон, которому должны следовать модульные тесты. Стандартное название этого шаблона проектирования — « шаблон AAA» , что расшифровывается как Arrange, Act, Assert (Расположить, Действовать, Утвердить).

Мне такое наименование не кажется интуитивно понятным, поэтому я использую GIVEN, RUN, TEST.

2. Организация: подготовка к тесту

# ДАННО user_df = pd.DataFrame({ 'user_id': [1, 2, 3], 'name': [«Джон», «Джейн», «Боб»] }) transaction_df = pd.DataFrame({ 'user_id': [1, 2, 3], 'product_id': [1, 1, 2], 'extra-column1-str': ['1', '2', '3'], 'extra-column2-int': [4, 5, 6], 'extra-column3-float': [1.1, 2.2, 3.3], }) product_df = pd.DataFrame({ 'product_id': [1, 2], 'product_name': [«яблоко», «банан»] }) expected_df = pd.DataFrame({ 'user_id': [1, 2, 3], 'product_id': [1, 1, 2], 'extra-column1-str': ['1', '2', '3'], 'extra-column2-int': [4, 5, 6], 'extra-column3-float': [1.1, 2.2, 3.3], 'name': [«Джон», «Джейн», «Боб»], 'product_name': [«яблоко», «яблоко», «банан»], })

Во-вторых, мы определяем входные данные функции и ожидаемые выходные данные. Здесь мы закладываем наши ожидания относительно того, как будут выглядеть входные данные и как должны выглядеть выходные данные.

Как видите, нам не нужно определять каждую отдельную функцию, которую мы ожидаем запустить, а только те, которые важны для теста.

Например, transaction_df правильно определяет столбцы user_id, product_id, а также добавляет три столбца разных типов (str, int, float), чтобы имитировать тот факт, что будут и другие столбцы.

То же самое касается product_df и user_df, хотя предполагается, что эти таблицы будут таблицами измерений, поэтому достаточно будет определить только столбцы name и product_name.

3. Действие: запуск функции для тестирования.

# RUN output_df = create_user_product_transaction_dataset( user_df, transaction_df, product_df )

В-третьих, мы запускаем функцию с определенными нами входными данными и собираем выходные данные.

4. Подтверждение: проверьте, соответствует ли результат ожидаемому.

# ТЕСТ pd.testing.assert_frame_equal( output_df, expected_df )

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

Обратите внимание: мы используем тестовый модуль Pandas, поскольку сравниваем фреймы данных Pandas. Для фреймов данных, отличных от Pandas, можно использовать оператор assert.

Полный код тестирования будет выглядеть так:

import pandas as pd def test_create_user_product_transaction_dataset(): # ДАННОЕ user_df = pd.DataFrame({ 'user_id': [1, 2, 3], 'name': [«Джон», «Джейн», «Боб»] }) transaction_df = pd.DataFrame({ 'user_id': [1, 2, 3], 'product_id': [1, 1, 2], 'transaction_date': [«2021-01-01», «2021-01-01», «2021-01-01»], 'extra-column1': [1, 2, 3], 'extra-column2': [4, 5, 6], }) product_df = pd.DataFrame({ 'product_id': [1, 2], 'product_name': [«apple», «banana»] }) expected_df = pd.DataFrame({ 'user_id': [1, 2, 3], 'product_id': [1, 1, 2], 'transaction_date': [«2021-01-01», «2021-01-01», «2021-01-01»], 'extra-column1': [1, 2, 3], 'extra-column2': [4, 5, 6], 'name': [«John», «Jane», «Bob»], 'product_name': [«apple», «apple», «banana»], }) # ВЫПОЛНИТЬ output_df = create_user_product_transaction_dataset( user_df, transaction_df, product_df ) # ТЕСТ pd.testing.assert_frame_equal( output_df, expected_df )

Чтобы лучше организовать тесты и сделать их чище, вы можете начать использовать комбинацию классов, фикстур и параметризации.

Подробное рассмотрение каждой из этих концепций выходит за рамки данной статьи, поэтому для тех, кому это интересно, я предоставляю руководство pytest How-To в качестве справочного материала по этим концепциям.

4c72809dd6d151d8cf60cfcf05952380

Что тестировать?

Теперь, когда мы создали модульный тест для одной функции, обратим внимание на оставшиеся функции. Внимательные читатели наверняка задумаются:

«Ого, мне что, по каждому предмету тест писать? Это же такая работа!»

Да, это правда. Это дополнительный код, который нужно писать и поддерживать.

Но хорошая новость в том, что не обязательно тестировать абсолютно все, но вам нужно знать, что важно в контексте вашей работы.

Ниже я приведу несколько практических правил и соображений, которые я принимаю во внимание, решая, что именно тестировать и почему.

1. Имеет ли код решающее значение для результата проекта?

В проекте по науке о данных существуют критические моменты, которые имеют решающее значение для успеха проекта по науке о данных. Многие из них обычно приходятся на этапы подготовки данных и оценки/объяснения модели .

Хорошим примером является пример теста, который мы видели выше в функции create_user_product_transaction_dataset.

Этот набор данных ляжет в основу всей последующей деятельности по моделированию.

Если соединение пользователь -> продукт каким-либо образом неверно, то это повлияет на все, что мы делаем дальше.

Поэтому стоит потратить время и убедиться, что этот код работает правильно.

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

Пример

Предположим, что соединение необходимо переписать для повышения эффективности использования памяти.

После внесения изменений модульный тест гарантирует, что выходные данные останутся прежними.

Если что-то было непреднамеренно изменено таким образом, что выходные данные стали выглядеть иначе (пропали строки, столбцы, изменились типы данных), тест немедленно укажет на проблему.

2. Использует ли код в основном сторонние библиотеки?

Возьмем в пример функцию загрузки данных:

def load_data(user_path: str, transaction_path: str, product_path: str): «»»Загрузить данные из указанных путей»»» df1 = pd.read_csv(user_path) df2 = pd.read_parquet(transaction_path) df3 = pd.read_parquet(product_path) return df1, df2, df3

Эта функция инкапсулирует процесс чтения данных из различных файлов. По сути, она вызывает три функции загрузки библиотеки Pandas.

Главная ценность этого кода — инкапсуляция.

Между тем, в нем нет никакой бизнес-логики, и, по моему мнению, область действия функции настолько специфична, что не стоит ожидать добавления какой-либо логики в будущем.

Если это так, то имя функции следует изменить, поскольку она делает больше, чем просто загрузку данных.

Поэтому эта функция не требует модульного тестирования.

Модульный тест для этой функции будет просто проверкой того, что pandas работает правильно, и мы можем быть уверены, что pandas протестировал свой собственный код.

3. Изменится ли код со временем?

Этот момент уже подразумевался в пунктах 1 и 2. Возможно, с точки зрения удобства обслуживания это наиболее важное соображение.

Вы должны подумать:

  • Насколько сложен код? Существует ли много способов добиться одного и того же результата?
  • Что может побудить кого-то изменить этот код? Подвержен ли источник данных изменениям в будущем?
  • Понятен ли код? Есть ли особенности поведения, которые можно легко упустить из виду при рефакторинге?

Возьмем в пример create_user_product_transaction_dataset.

  • В будущем схема входных данных может претерпеть изменения.
  • Возможно, набор данных станет больше, и нам придется разбить слияние на несколько этапов из соображений производительности.
  • Возможно, необходимо временно применить грязный хак для обработки пустых значений из-за проблем с источником данных.

В каждом случае может потребоваться изменение базового кода, и каждый раз нам нужно гарантировать, что выходные данные не изменятся.

В отличие от этого, load_data не делает ничего, кроме загрузки данных из файла.

Не думаю, что это сильно изменится в будущем, разве что изменится формат файла. Поэтому я бы отложил написание теста до тех пор, пока не произойдёт существенное изменение в исходном источнике данных (что, скорее всего, потребовало бы значительного изменения конвейера).

Где размещать тесты и как их проводить

До сих пор мы рассмотрели, как писать тестируемый код и как создавать сами тесты.

Теперь давайте рассмотрим, как структурировать ваш проект, включив в него тесты, и как эффективно их проводить.

Структура проекта

Как правило, проект по науке о данных может иметь следующую структуру:

<имя-проекта> |— data # где хранятся данные |— conf # где хранятся файлы конфигурации для ваших конвейеров |— src # здесь хранится весь код для репликации вашего проекта |— notebooks # здесь хранится весь код для одноразовых экспериментов, исследований и т. д. |— tests # здесь хранятся все тесты |— pyproject.toml |— README.md |— requirements.txt

Папка src должна содержать весь код проекта, имеющий решающее значение для его реализации.

Общее практическое правило

Если код, который вы планируете запускать несколько раз (с разными входными данными или параметрами), его следует поместить в папку src.

Вот несколько примеров:

  • обработка данных
  • проектирование функций
  • модель обучения
  • оценка модели

Между тем, все, что является единичными фрагментами анализа, может находиться в блокнотах Jupyter и храниться в папке блокнотов.

Это в первую очередь включает в себя

  • ЕДА
  • эксперименты с ad-hoc-моделью
  • анализ объяснений локальной модели

Почему?

Потому что блокноты Jupyter, как известно, нестабильны, сложны в управлении и тестировании. Мы не хотим повторно запускать критически важный код через блокноты.

Структура тестовой папки

Предположим, ваша папка src выглядит так:

src |— pipelines |— data_processing.py |— feature_engineering.py |— model_training.py |— __init__.py

Каждый файл содержит функции и конвейеры, аналогичные примеру, который мы видели выше.

Тестовая папка должна выглядеть следующим образом:

тесты |— конвейеры |— test_data_processing.py |— test_feature_engineering.py |— test_model_training.py

где тестовый каталог отражает структуру каталога src, а каждый файл начинается с префикса test_.

Причина этого проста:

  • Тесты для заданного файла найти легко, поскольку структура папки test отражает структуру папки src.
  • Он надежно отделяет тестовый код от исходного кода.

Проведение тестов

После настройки тестов, как указано выше, вы можете запустить их различными способами:

1. Через терминал

pytest -v

2. Через редактор кода

Я использую это во всех своих проектах.

Visual Studio Code — мой любимый редактор. Он автоматически обнаруживает тесты и его очень легко отлаживать.

Прочитав документы, я не думаю, что есть смысл повторять их содержание, поскольку оно и так достаточно понятно, поэтому вот ссылка:

  • Документация по тестированию VSCode

Аналогично, большинство редакторов кода также имеют схожие возможности, поэтому нет никаких оправданий тому, чтобы не писать тесты.

Это действительно просто: прочитайте документацию и приступайте.

3. Через конвейер непрерывной интеграции (например, GitHub Actions, Gitlab и т. д.)

Легко настроить автоматический запуск тестов по запросам на включение изменений через GitHub.

Идея заключается в том, что всякий раз, когда вы делаете PR, система автоматически найдет и выполнит тесты для вас.

Это означает, что даже если вы забудете запустить тесты локально через 1 или 2, они всегда будут запущены, когда вы захотите объединить свои изменения.

Опять же, нет смысла мне перечислять документы; вот ссылка

  • Документация по сборке и тестированию Github

Конечная цель, которую мы хотим достичь

Исходя из вышеизложенных инструкций, я думаю, что будет лучше потратить наше время и подчеркнуть некоторые важные моменты относительно того, чего мы хотим достичь с помощью автоматизированных тестов, а не просто повторять инструкции, которые вы можете найти по ссылкам выше.

В первую очередь , автоматизированные тесты пишутся для того, чтобы повысить доверие к вашему коду и свести к минимуму человеческие ошибки.

Это делается в интересах:

  • Сам
  • Ваша команда
  • и бизнес в целом.

Поэтому, чтобы по-настоящему извлечь максимальную пользу из написанных вами тестов, вам необходимо заняться настройкой конвейера непрерывной интеграции.

Огромное значение имеет возможность забыть запустить тесты локально и при этом быть уверенным в том, что тесты будут запущены, когда вы создадите PR или внесете какие-то изменения.

Вы не хотите стать лицом, ответственным за ошибку, которая приводит к производственному инциденту из-за того, что вы забыли запустить тесты, или тем, кто пропустил ошибку во время проверки PR.

Так что, если вы пишете тесты, пожалуйста, потратьте немного времени на настройку конвейера непрерывной интеграции. Прочитайте документацию на GitHub, умоляю вас. Настройка проста, и она сотворит чудеса.

Заключительные замечания

Прочитав эту статью, я надеюсь, она произвела на вас впечатление.

  1. Важность написания тестов, особенно в контексте науки о данных
  2. Насколько легко их писать и запускать

Но есть еще одна причина, по которой вам следует знать, как писать автоматизированные тесты.

Эта причина в том, что

Наука о данных меняется .

Раньше наука о данных в основном сводилась к проверке концепций, созданию моделей в блокнотах Jupyter и отправке их инженерам для внедрения. Тем временем специалисты по данным приобрели дурную славу из-за написания ужасного кода.

Но теперь эта отрасль повзрослела.

По мере развития ML-Ops и ML-инжиниринга становится все проще быстро создавать и развертывать модели.

Таким образом,

  • построение модели
  • развертывание
  • переподготовка
  • обслуживание

становится задачей инженеров машинного обучения.

В то же время обработка данных, которой мы раньше занимались, становится настолько сложной, что теперь ею занимаются специальные группы специалистов по обработке данных.

В результате наука о данных занимает очень узкое пространство между этими двумя дисциплинами, и довольно скоро границы между специалистом по работе с данными и аналитиком данных сотрутся.

Траектория такова, что специалисты по анализу данных больше не будут создавать передовые модели, а станут больше концентрироваться на бизнесе и продуктах, генерируя аналитические данные и отчеты MI.

Если вы хотите оставаться ближе к созданию модели, вам больше не достаточно просто писать код.

Вам нужно научиться правильно писать код и поддерживать его в хорошем состоянии. Машинное обучение — уже не новинка, это уже не просто PoC, это становится разработкой программного обеспечения.

Если вы хотите узнать больше

Если вы хотите узнать больше о навыках разработки программного обеспечения, применяемых в науке о данных, вот несколько статей по теме:

  • Наследование: концепция программной инженерии, которую специалисты по данным должны знать для достижения успеха
  • Инкапсуляция: концепция программной инженерии, которую специалисты по данным должны знать для достижения успеха
  • DSLP — фреймворк управления проектами в области науки о данных, который изменил мою команду

Вы также можете стать членом команды на Patreon здесь!

Для всех статей у нас есть специальные темы для обсуждения: задавайте вопросы об автоматизированном тестировании, обсуждайте тему более подробно и делитесь опытом с другими специалистами по данным. Обучение на этом не заканчивается.

Специальную ветку обсуждения этой статьи вы можете найти здесь.

Источник: towardsdatascience.com

✅ Найденные теги: Автоматизированное, новости
Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

Фото сгенерированных лиц: исследование показывает, что люди не могут отличить настоящие лица от сгенерированных
Нейросети построили капитализм за трое суток: 100 агентов Claude заперли…
Скетч: цифровой осьминог и виртуальный мир внутри компьютера с человечком.
Сцена с жестами пальцами, где один жест символизирует "VPN", а другой "KHP".
‼️Paramount купила Warner Bros. Discovery — сумма сделки составила безумные…
Скриншот репозитория GitHub "Claude Scientific Skills" AI для научных исследований.
Структура эффективного запроса Claude с элементами задачи, контекста и референса.
Эскиз и готовая веб-страница платформы для AI-дизайна в современном темном режиме.
ideipro logotyp
Image Not Found
Звёздное небо с галактиками и туманностями, космос, Вселенная, астрофотография.

Система оповещения обсерватории Рубина отправила 800 000 сигналов в первую ночь наблюдений.

Астрономы будут получать оповещения о небесных явлениях в течение нескольких минут после их обнаружения. Теренс О'Брайен, редактор раздела «Выходные». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной…

Мар 2, 2026
Женщина с длинными тёмными волосами в синем свете, нейтральный фон.

Расследование в отношении 61-фунтовой машины, которая «пожирает» пластик и выплевывает кирпичи.

Обзор компактного пресса для мягкого пластика Clear Drop — и что будет дальше. Шон Холлистер, старший редактор Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Мар 2, 2026
Черный углеродное волокно с текстурой плетения, отражающий свет.

Материал будущего: как работает «бессмертный» композит

Учёные из Университета штата Северная Каролина представили композит нового поколения, способный самостоятельно восстанавливаться после серьёзных повреждений.  Речь идёт о модифицированном армированном волокном полимере (FRP), который не просто сохраняет прочность при малом весе, но и способен «залечивать» внутренние…

Мар 2, 2026
Круглый экран с изображением замка и горы, рядом электронная плата.

Круглый дисплей Waveshare для креативных проектов

Круглый 7-дюймовый сенсорный дисплей от Waveshare создан для разработчиков и дизайнеров, которым нужен нестандартный экран.  Это IPS-панель с разрешением 1 080×1 080 пикселей, поддержкой 10-точечного ёмкостного сенсора, оптической склейкой и защитным закалённым стеклом, выполненная в круглом форм-факторе.…

Мар 2, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых