Что такое модульное тестирование (unit testing)?

Ответ

Модульное тестирование — это метод проверки корректности работы отдельных, изолированных единиц кода (юнитов), таких как функции, методы или классы.

Основные принципы:

  • Изоляция: Тестируемый юнит изолируется от зависимостей (баз данных, сетевых вызовов, других модулей) с помощью заглушек (mocks/stubs).
  • Детерминированность: Тест всегда должен давать одинаковый результат при одинаковых входных данных.
  • Скорость: Модульные тесты выполняются быстро, что позволяет запускать их часто.
  • Автоматизация: Тесты являются частью кодовой базы и запускаются автоматически (в CI/CD).

Цель: Раннее обнаружение дефектов, упрощение рефакторинга и создание живой документации к коду.

Пример на Java с JUnit и Mockito:

// Класс для тестирования (сервис)
public class PaymentService {
    private final TaxCalculator taxCalculator;

    public PaymentService(TaxCalculator taxCalculator) {
        this.taxCalculator = taxCalculator;
    }

    public double calculateTotal(double amount) {
        double tax = taxCalculator.calculateTax(amount);
        return amount + tax;
    }
}

// Модульный тест
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class PaymentServiceTest {
    @Test
    void calculateTotal_ShouldReturnSumOfAmountAndTax() {
        // 1. ARRANGE: Подготовка данных и заглушек
        double amount = 100.0;
        double mockTax = 20.0;
        TaxCalculator mockCalculator = mock(TaxCalculator.class);
        when(mockCalculator.calculateTax(amount)).thenReturn(mockTax);
        PaymentService service = new PaymentService(mockCalculator);

        // 2. ACT: Вызов тестируемого метода
        double result = service.calculateTotal(amount);

        // 3. ASSERT: Проверка результата
        double expectedTotal = 120.0; // 100 + 20
        assertEquals(expectedTotal, result);
        // Проверка, что зависимость была вызвана с правильным аргументом
        verify(mockCalculator).calculateTax(amount);
    }
}

Популярные фреймворки: JUnit, TestNG (Java), pytest (Python), Jest (JavaScript). Модульные тесты — фундамент методологий TDD (Test-Driven Development) и CI/CD.

Ответ 18+ 🔞

А, ну модульное тестирование, блядь! Это ж когда ты каждую свою функцию, как псину дворовую, на поводок берёшь и проверяешь, не сбежит ли она с мозгами и не наложит ли в кодовую базу.

Основные принципы, ёпта:

  • Изоляция: Это святое! Отделяешь свой кусок кода от всей этой ебалы: баз данных, других сервисов, интернета — хуй его знает чего. Подсовываешь ему заглушки (эти ваши моки-стабы), будто он в вакууме работает.
  • Детерминированность: Чтоб как в аптеке, сука. Дал одни цифры — получил один ответ. Дал те же цифры через год — получил тот же самый ответ, а не "ой, а сегодня сервер полдня бухал".
  • Скорость: Они должны бегать, как ошпаренные! Чтобы ты их по сто раз на дню гонять мог, и тебе не было влом.
  • Автоматизация: Это не рукоблудие, блядь. Написал раз — и они сами в пайплайне бегают, как мартышки, и кричат, если что-то пошло не так.

Цель-то какая? Чтобы не получилось, как в том анекдоте: "работает? не трогай!". А тут ты трогаешь, рефакторишь, а эти тесты, как цепные псы, тебе сразу: "Э, куда пошёл? Сюда иди, ошибку нашёл, пидрила!". И ещё они как живая памятка — глянул на тест и понял, что эта функция вообще должна делать, а не гадать, как шаман.

Смотри, как это выглядит на Java, если не выёбываться:

// Вот наш сервис, который считает, сколько с тебя сдерут денег
public class PaymentService {
    private final TaxCalculator taxCalculator; // А это хуйня, которая налог насчитывает

    public PaymentService(TaxCalculator taxCalculator) {
        this.taxCalculator = taxCalculator;
    }

    public double calculateTotal(double amount) {
        double tax = taxCalculator.calculateTax(amount); // Спрашиваем у калькулятора
        return amount + tax; // И прибавляем
    }
}

// А вот наш тест, который всё это проверяет
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class PaymentServiceTest {
    @Test
    void calculateTotal_ShouldReturnSumOfAmountAndTax() {
        // 1. ПОДГОТОВКА (ARRANGE): Расставляем все фигурки по полочкам
        double amount = 100.0; // Сто рублей
        double mockTax = 20.0; // Двадцать рублей налога (с потолка взял)
        // Вот тут магия: делаем ПОДДЕЛЬНЫЙ калькулятор (mock), который не лезет в реальную систему
        TaxCalculator mockCalculator = mock(TaxCalculator.class);
        // Говорим ему: "Слушай сюда, мудила. Когда тебя спросят про налог для 100 рублей, отвечай — 20".
        when(mockCalculator.calculateTax(amount)).thenReturn(mockTax);
        // И пихаем эту заглушку в наш сервис
        PaymentService service = new PaymentService(mockCalculator);

        // 2. ДЕЙСТВИЕ (ACT): Нажимаем на кнопку
        double result = service.calculateTotal(amount);

        // 3. ПРОВЕРКА (ASSERT): Сверяем с ожиданием
        double expectedTotal = 120.0; // 100 + 20
        assertEquals(expectedTotal, result); // Сошлось? Красава.
        // И дополнительно тычем пальцем в заглушку: "А ты точно вызвался с аргументом 100?"
        verify(mockCalculator).calculateTax(amount);
    }
}

Инструменты, которые все используют: JUnit, TestNG (для явы), pytest (для питона), Jest (для жс). Это, блядь, фундамент, на котором TDD и CI/CD стоят. Без этого — как без штанов на людях, вроде идёшь, но все на жопу смотрят.