Ответ
Мой опыт охватывает несколько уровней тестирования и инструментов. В основном я работал с PHPUnit для модульного и интеграционного тестирования бизнес-логики приложений. Для изоляции зависимостей активно использовал моки и стабы.
Пример модульного теста для сервиса:
class PaymentServiceTest extends TestCase
{
private $paymentGatewayMock;
private $paymentService;
protected function setUp(): void
{
$this->paymentGatewayMock = $this->createMock(PaymentGatewayInterface::class);
$this->paymentService = new PaymentService($this->paymentGatewayMock);
}
public function testProcessPaymentSuccess(): void
{
$this->paymentGatewayMock
->expects($this->once())
->method('charge')
->with(100.00, 'tok_123')
->willReturn(new PaymentResult(true, 'txn_456'));
$result = $this->paymentService->process(100.00, 'tok_123');
$this->assertTrue($result->isSuccess());
$this->assertEquals('txn_456', $result->getTransactionId());
}
}
Для тестирования API (REST, GraphQL) использовал Codeception в связке с PHPUnit, что позволяло удобно описывать сценарии и проверять ответы. Также интегрировал тесты в CI/CD (GitLab CI, GitHub Actions), настраивая автоматический запуск на каждый пулл-реквест. Для E2E-тестирования критичных пользовательских сценариев применял Selenium WebDriver через Codeception, хотя основное внимание уделял поддержанию быстрых и стабильных модульных и интеграционных тестов.
Ответ 18+ 🔞
Слушай, а я-то тут сижу и думаю — ну и что ты за тестировщик, если не можешь объяснить это так, чтобы даже бабушка у подъезда поняла? Ладно, давай разберём.
Вот смотри, я в этой теме собаку съел, и не одну. Основная моя работа — это PHPUnit, ну, для всяких мелких штук (модульное тестирование) и когда эти штуки начинают друг с другом общаться (интеграционное). Главная задача — отлавливать косяки в логике приложения, пока они не вылезли пользователю в виде кривой рожи на экране. А чтобы не тащить в тесты всю эту ебучую инфраструктуру — базы, внешние сервисы — я юзаю моки и стабы. Это такие заглушки, которые притворяются реальными объектами, но делают только то, что я им скажу. Удобно, блядь, как.
Вот, например, смотри на этот кусок кода. Это классика жанра — тест сервиса оплаты. Смысл в том, чтобы проверить, что если платёжный шлюз (это та внешняя хрень, которая реально списывает деньги) отработал как надо, то и наш сервис всё правильно обработает.
class PaymentServiceTest extends TestCase
{
private $paymentGatewayMock;
private $paymentService;
protected function setUp(): void
{
$this->paymentGatewayMock = $this->createMock(PaymentGatewayInterface::class);
$this->paymentService = new PaymentService($this->paymentGatewayMock);
}
public function testProcessPaymentSuccess(): void
{
$this->paymentGatewayMock
->expects($this->once())
->method('charge')
->with(100.00, 'tok_123')
->willReturn(new PaymentResult(true, 'txn_456'));
$result = $this->paymentService->process(100.00, 'tok_123');
$this->assertTrue($result->isSuccess());
$this->assertEquals('txn_456', $result->getTransactionId());
}
}
Видишь, что тут происходит? Я создаю муляж платёжного шлюза. Потом говорю ему: «Слушай сюда, мудила. Когда у тебя вызовут метод charge с суммой 100 и токеном tok_123 — ты сразу, без вопросов, возвращай успешный результат с номером транзакции txn_456. И вызван ты должен быть ровно один раз, я слежу!». Потом я прогоняю через наш сервис эти же данные и проверяю, что он вернул мне именно тот успешный результат, который я ему подсунул через заглушку. Если что-то не сходится — значит, логика в сервисе поехала, и её надо чинить. Элегантно, ёпта!
А ещё я часто Codeception использовал, особенно когда надо было API потрогать — REST или эту вашу моду GraphQL. Он поверх PHPUnit, но сценарии там описывать — одно удовольствие, всё чётко и по полочкам. И, конечно, без CI/CD никуда — настраивал автоматический запуск всей этой тестовой оргии на каждый чих в GitLab CI или GitHub Actions. Чтобы если кто-то сломал — сразу красная лампочка загоралась и все бегали как угорелые.
Ну и для полного счастья, для самых важных пользовательских сценариев (типа «зарегистрировался — купил — получил»), иногда подключал Selenium WebDriver, тоже через Codeception. Но, честно говоря, это такие тесты — овердохуища времени жрут и хрупкие как хрустальная ваза твоей бабушки. Поэтому основной упор всегда был на быстрые и стабильные модульные тесты. Чтобы не бздеть каждый раз, когда делаешь мелкое изменение. Вот так-то.