BMC IO DocsBMC IO Docs
IO v6
IO v7
Notes
Docs
IO v6
IO v7
Notes
Docs
  • IO Framework v7

    • О фреймворке
    • Что изменилось в 7 версии
    • IO7 Framework vs Laravel 13 — Сравнение
    • Структура
    • Варианты проектов
    • Доступ к участкам проекта
    • Разворачивание нового проекта
    • Установка проекта
    • Запуск локальной разработки
  • API

    • Обзор API
    • IOCore::call() и IOCore::callAction()
    • Создание API
  • CLI

    • Обзор CLI
    • iocli
    • Кастомные CLI команды
  • Работа с базами данных

    • Обзор работы с базами данных
    • Cron / Планировщик задач (Schedule)
    • Фабрики
    • Миграции
    • Сидеры
    • Query Builder
    • Подключение MinIO
    • Подключение MongoDB
    • Подключение MySQL
    • Подключение PostgreSQL
    • Подключение ProxySQL
    • Подключение RabbitMQ
    • Подключение Redis
  • Логирование

    • Обзор логирования
    • IOEvent - Логирование событий
    • Pinba (PinbaManager) - Профилировщик
  • Модели

    • Обзор моделей
    • CRUD операции
    • Связи моделей
  • Очереди

    • Обзор очередей
    • RabbitMQ
  • Маршруты

    • Обзор маршрутов
    • Определение маршрутов
    • Middleware
  • Сессии

    • Обзор сессий
    • Кроссдоменная SSO
    • JWT авторизация
    • Социальная авторизация
    • OAuth2
    • Keycloak
  • Twig

    • Обзор Twig
    • Формы (FormBuilder)
    • Twig функции
  • Тестирование

    • Обзор тестирования
    • Unit тесты
  • Backend

    • Настройки проекта Conf.php
    • Настройки модуля Module.php
    • Настройки composer
    • Функции фреймворка
    • Вспомогательные функции (Helpers)
    • Глобальные переменные
    • Константы фреймворка
    • Event класс
    • Core класс
    • Route и работа с ними
    • Request и работа с ними
    • Middleware и работа с ними
    • Class и работа с ними
    • Api model и работа с ними
    • Template и работа с ними
    • Мультиязычность и словарь фраз
    • Bin скрипты и работа с ними
    • Cron скрипты и работа с ними
    • $ioSession и работа с сессией
    • $ioHash и работа с кешем
    • $ioXCache и работа с кешем
    • $iodb и работа с базой данных
    • Миграции
  • Frontend

    • Twig шаблонизатор и работа с ним
    • Twig функции
    • Twig фильтры
    • Twig контекст
    • Настройка NPM
    • Сборка Frontend составляющей
    • Обращение к Api через JS
    • Хранилище (store)
    • Навигация в хосте
    • Навигация в микрофронтенде
    • Параметры микрофронтенда
    • Проверка полномочий
  • Tailux UI Компоненты

    • Базовый UI

      • AvatarDot
      • Avatar
      • Badge
      • Button
      • Card
      • ConfirmModal
      • Divider
      • Highlight
      • Tag
    • Загрузка

      • Circlebar
      • Progress
      • Skeleton
      • Spinner
    • Навигация

      • Accordion
      • AccordionButton
      • AccordionHeader
      • AccordionItem
      • AccordionPanel
      • AccordionProvider
      • Pagination
      • PaginationControl
      • PaginationItems
      • PaginationDots
      • PaginationFirst
      • PaginationLast
      • PaginationNext
      • PaginationPrevious
    • Отображение данных

      • Box
      • Collapse
      • ContextualHelp
      • ScrollShadow
      • Timeline
      • TimelineItem
    • Элементы таблиц

      • Table
      • CollapsibleSearch
      • ColumnFilter
      • CopyableCell
      • DateFilter
      • FacetedFilter
      • FilterSelector
      • PaginationSection
      • RadioFilter
      • RangeFilter
      • ResponsiveFilter
      • RoleFilter
      • SelectedRowsActions
      • TableColumnVisibility
      • TableSettings
      • TableSortIcon
    • Элементы форм

      • Checkbox
      • Combobox
      • DatePicker
      • Filepond
      • Input
      • Listbox
      • Radio
      • Range
      • Select
      • StyledSwitch
      • Swap
      • SwapOn
      • SwapOff
      • Switch
      • Textarea
      • TextEditor
      • Upload

Unit тесты — IO7 Framework

Внимание

Данная статья документации написана нейросетью. Возможны некоторые расхождения и неточность информации!

  • Обзор
  • Структура
  • Базовый класс TestCase
    • Методы TestCase
  • Запуск
  • Примеры тестов
    • TokenManagerTest
    • EventTest
    • ContainerTest
  • Создание своих тестов
    • 1. Создайте файл теста
    • 2. Напишите тест
    • 3. Запустите тест
  • Моки и стабы
  • Тестирование с контейнером
  • Конфигурация CI

Обзор

Unit тесты проверяют отдельные компоненты фреймворка в изоляции. IO7 включает готовые unit тесты для ключевых компонентов: авторизация, база данных, контейнер, логирование, профилирование.

Требования: PHP ^8.2, Illuminate (Laravel 13), PHPUnit 11

Структура

app/Testing/
├── Unit/
│   ├── Auth/
│   │   └── TokenManagerTest.php
│   ├── Database/
│   │   ├── DatabaseManagerTest.php
│   │   ├── ConnectionManagerTest.php
│   │   └── SqlConnectionTest.php
│   ├── Console/
│   │   └── CommandTest.php
│   ├── Profile/
│   │   └── PinbaManagerTest.php
│   ├── EventTest.php
│   ├── FormBuilderTest.php
│   └── ContainerTest.php
├── Feature/
│   └── ExampleTest.php
└── UserTest.php

Базовый класс TestCase

Базовый класс для тестов находится во фреймворке: IO\Testing\TestCase

namespace App\Testing\Unit;

use IO\Testing\TestCase;

class MyTest extends TestCase
{
    public function testSomething(): void
    {
        $result = $this->getIodb()->search('users', []);
        $this->assertNotEmpty($result['res']);
    }
}

Методы TestCase

МетодВозвратОписание
getApp()\IO\AppПолучить экземпляр App
getIodb()\IO\Database\SqlConnectionПолучить SqlConnection

Запуск

# Все unit тесты
php vendor/bin/phpunit --testsuite Unit

# Один файл
php vendor/bin/phpunit app/Testing/Unit/Auth/TokenManagerTest.php

# Один метод
php vendor/bin/phpunit app/Testing/Unit/Auth/TokenManagerTest.php::testCreateAccessToken

Примеры тестов

TokenManagerTest

<?php
namespace Tests\Unit;

use Tests\TestCase;
use PHPUnit\Framework\Attributes\Test;

class TokenManagerTest extends TestCase
{
    #[Test]
    public function testCreateTokenManager(): void
    {
        $manager = \IO\Auth\TokenManager::init([
            'secret_key' => 'test_secret_key_12345',
            'access_ttl' => 3600,
            'refresh_ttl' => 2592000,
        ]);

        $this->assertInstanceOf(\IO\Auth\TokenManager::class, $manager);
    }

    #[Test]
    public function testCreateAccessToken(): void
    {
        $manager = \IO\Auth\TokenManager::init([
            'secret_key' => 'test_secret_key_12345',
            'access_ttl' => 3600,
        ]);

        $token = $manager->createAccessToken('user_123', ['read', 'write']);
        $this->assertIsString($token);
        $this->assertNotEmpty($token);
    }

    #[Test]
    public function testValidateAccessToken(): void
    {
        $manager = \IO\Auth\TokenManager::init([
            'secret_key' => 'test_secret_key_12345',
            'access_ttl' => 3600,
        ]);

        $token = $manager->createAccessToken('user_123', ['read']);
        $payload = $manager->validateAccessToken($token);

        $this->assertIsArray($payload);
        $this->assertEquals('user_123', $payload['user_id']);
    }

    #[Test]
    public function testValidateInvalidToken(): void
    {
        $manager = \IO\Auth\TokenManager::init([
            'secret_key' => 'test_secret_key_12345',
        ]);

        $payload = $manager->validateAccessToken('invalid_token');
        $this->assertNull($payload);
    }
}

EventTest

<?php
namespace Tests\Unit;

use Tests\TestCase;
use PHPUnit\Framework\Attributes\Test;

class EventTest extends TestCase
{
    #[Test]
    public function testLogLevelConstants(): void
    {
        $this->assertEquals(0, \IO\Event::LOG_DEBUG);
        $this->assertEquals(1, \IO\Event::LOG_INFO);
        $this->assertEquals(2, \IO\Event::LOG_WARNING);
        $this->assertEquals(3, \IO\Event::LOG_ERROR);
        $this->assertEquals(4, \IO\Event::LOG_CRITICAL);
    }

    #[Test]
    public function testAddHandler(): void
    {
        \IO\Event::addHandler('custom', function ($level, $message, $context): mixed {
            return $message;
        });
        $this->assertTrue(true);
    }

    #[Test]
    public function testRemoveHandler(): void
    {
        \IO\Event::addHandler('test_handler');
        \IO\Event::removeHandler('test_handler');
        $this->assertTrue(true);
    }
}

ContainerTest

<?php
namespace Tests\Unit;

use Tests\TestCase;
use PHPUnit\Framework\Attributes\Test;

class ContainerTest extends TestCase
{
    #[Test]
    public function testBindAndGet(): void
    {
        $container = new \IO\Container();
        $container->bind('foo', fn (): string => 'bar');

        $this->assertEquals('bar', $container->get('foo'));
    }

    #[Test]
    public function testSingleton(): void
    {
        $container = new \IO\Container();
        $container->singleton('service', fn (): \stdClass => new \stdClass());

        $a = $container->get('service');
        $b = $container->get('service');

        $this->assertSame($a, $b);
    }

    #[Test]
    public function testHas(): void
    {
        $container = new \IO\Container();
        $container->bind('foo', 'bar');

        $this->assertTrue($container->has('foo'));
        $this->assertFalse($container->has('baz'));
    }
}

Создание своих тестов

1. Создайте файл теста

php iocli make:test MyComponentTest

2. Напишите тест

<?php
namespace Tests\Unit;

use Tests\TestCase;
use PHPUnit\Framework\Attributes\Test;

class MyComponentTest extends TestCase
{
    #[Test]
    public function testSomething(): void
    {
        $result = myFunction();
        $this->assertEquals('expected', $result);
    }

    #[Test]
    public function testWithMock(): void
    {
        $mock = $this->createMock(SomeService::class);
        $mock->method('getData')
            ->willReturn(['key' => 'value']);

        $component = new MyComponent($mock);
        $result = $component->process();

        $this->assertEquals('value', $result['key']);
    }

    #[Test]
    public function testException(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Invalid argument');

        new MyClass('invalid');
    }
}

3. Запустите тест

php vendor/bin/phpunit tests/Unit/MyComponentTest.php

Моки и стабы

// Создание мока
$mock = $this->createMock(SomeService::class);

// Настройка возвращаемого значения
$mock->method('getData')
    ->willReturn(['key' => 'value']);

// Проверка вызова
$mock->expects($this->once())
    ->method('save')
    ->with($this->equalTo($data));

// Spy (проверка что метод был вызван)
$mock->expects($this->atLeastOnce())
    ->method('log');

Тестирование с контейнером

#[Test]
public function testWithContainer(): void
{
    $container = new \IO\Container();
    $container->setTestMode();

    // Подмена сервиса
    $container->instance('iodb', $mockDb);

    $app = new \IO\App($container);
    $result = $app->get('iodb');

    $this->assertSame($mockDb, $result);
}

Конфигурация CI

# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
      - run: composer install
      - run: composer test:unit
Prev
Обзор тестирования