При тестировании часто необходимо привести состояние окружающего мира в определённое состояние, то есть создать для теста тестовое окружение (Fixture). В PHPUnit для этого есть методы setUp() и tearDown(). Перед запуском тестового метода запускается метод setUp(). В этом методе вы можете настраивать тестовое окружение: создавать объекты, с которыми тестируете и т.п. После выполнения тестового метода вызывается tearDown(), вне зависимости успешно прошёл тест или нет. В этом методе можно возвращать тестовое окружение в первоначальное состояние, очищать объекты, с которыми тестируете и т.п.
Приведём пример. Нужно протестировать класс SomeController:
<?php // src/ExampleProject/SomeController.php namespace ExampleProject; class SomeController { /** * @var StorageInterface */ protected $storage; /** * @param StorageInterface $storage */ public function __construct(StorageInterface $storage) { $this->storage = $storage; } /** * @param array $data * @return array * @throws \Exception */ public function saveAction(array $data): array { if (!$this->storage->save($data)) { throw new \Exception(); } return []; } }
Код теста как и ранее (см. PHPUnit mock-объекты):
<?php // tests/ExampleProject/SomeControllerTest.php namespace Tests\ExampleProject; use PHPUnit\Framework\TestCase; use ExampleProject\SomeController; use ExampleProject\StorageInterface; class SomeControllerTest extends TestCase { /** * @throws \Exception */ public function testSaveAction() { $data = ['a', 'b']; /** @var StorageInterface | \PHPUnit_Framework_MockObject_MockObject $storageMock */ $storageMock = $this->createMock(StorageInterface::class); $storageMock->expects($this->once()) ->method('save') ->with($data) ->will($this->returnValue(true)); $someController = new SomeController($storageMock); $this->assertInternalType('array', $someController->saveAction($data)); } /** * @expectedException \Exception */ public function testSaveActionFailSaving() { /** @var StorageInterface | TestCase $storageMock */ $storageMock = $this->createMock(StorageInterface::class); $storageMock->method('save') ->willReturn(false); $someController = new SomeController($storageMock); $someController->saveAction(['any data']); } }
В каждом тесте создаётся mock-объект хранилища и объект контроллера. Это и дублирование кода и отвлекает внимание от сути теста.
Доработаем тест:
<?php // tests/ExampleProject/SomeControllerTest.php namespace Tests\ExampleProject; use PHPUnit\Framework\TestCase; use ExampleProject\SomeController; use ExampleProject\StorageInterface; class SomeControllerTest extends TestCase { /** * @var StorageInterface | \PHPUnit_Framework_MockObject_MockObject */ protected $storageMock; /** * @var SomeController */ protected $someController; /** * */ public function setUp() { $this->storageMock = $this->createMock(StorageInterface::class); $this->someController = new SomeController($this->storageMock); } /** * @throws \Exception */ public function testSaveAction() { $data = ['a', 'b']; $this->storageMock->expects($this->once()) ->method('save') ->with($data) ->will($this->returnValue(true)); $this->assertInternalType('array', $this->someController->saveAction($data)); } /** * @expectedException \Exception */ public function testSaveActionFailSaving() { $this->storageMock->method('save')->willReturn(false); $this->someController->saveAction(['any data']); } }
В новом варианте тестовые методы значительно упростились и отсутствует дублирование кода.
Помимо методов setUp() и tearDown() есть также статические методы setUpBeforeClass() и tearDownAfterClass(). Метода setUpBeforeClass() запускается перед первым запуском первого тестового метода, а tearDownAfterClass() запускается после выполнения всех тестовых методов.