# 12. Тестирование
[← Оглавление](./index.md)
---
## Код должен быть тестируемым — изолируйте зависимости
**ПЛОХО — ПРЯМОЙ ВЫЗОВ API, НЕЛЬЗЯ ПРОТЕСТИРОВАТЬ**
```php
class LeadService {
public function create(array $data): int {
return \CCrmLead::Add($data); // нельзя замокать
}
}
```
**ХОРОШО — РЕПОЗИТОРИЙ ЧЕРЕЗ ИНТЕРФЕЙС**
```php
interface LeadRepository {
public function save(Lead $lead): void;
}
class LeadService {
public function __construct(private LeadRepository $repo) {}
public function create(LeadDto $dto): Lead {
$lead = new Lead($dto);
$this->repo->save($lead);
return $lead;
}
}
// PHPUnit тест:
class LeadServiceTest extends \PHPUnit\Framework\TestCase {
public function testCreateLead(): void {
$mockRepo = $this->createMock(LeadRepository::class);
$mockRepo->expects($this->once())->method('save');
$service = new LeadService($mockRepo);
$lead = $service->create(new LeadDto(title: 'Test Lead'));
$this->assertSame('Test Lead', $lead->getTitle());
}
}
```
---
## Data Providers для параметризованных тестов
**ПЛОХО — ДУБЛИРОВАНИЕ ТЕСТОВЫХ МЕТОДОВ**
```php
public function testSmallOrderDiscount(): void {
$this->assertSame(0.0, (new DiscountCalculator())->calculate(5000.0));
}
public function testMediumOrderDiscount(): void {
$this->assertSame(0.05, (new DiscountCalculator())->calculate(50000.0));
}
// и так далее...
```
**ХОРОШО — @DATAPROVIDER**
```php
class DiscountCalculatorTest extends \PHPUnit\Framework\TestCase {
/** @dataProvider discountProvider */
public function testCalculate(float $amount, float $expected): void {
$this->assertSame($expected, (new DiscountCalculator())->calculate($amount));
}
public static function discountProvider(): array {
return [
'small order' => [5000.0, 0.0],
'medium order' => [50000.0, 0.05],
'large order' => [150000.0, 0.15],
];
}
}
```