How to Instantly Migrate Nette\Tester to PHPUnit
This post was updated in November 2020 with fresh know-how.
What is new?
Switch from deprecated --set option to rector.php config.
We had 🍺 after PHP meetup in Prague and Tomas asked me:
"We don't use Nette, but we still have many tests in Tester. Can Rector migrate them to PHPUnit?"
"Hold my 🍺"
In the last post we looked on instant migration of PhpSpec to PHPUnit.
PhpSpec has a different architecture than PHPUnit - e.g.
- PHPUnit creates mocks in body of tested method
$this->createMock(), - but PhpSpec puts them in parameters
public function is_should_work(Category $categoryMock).
This has a huge influence on the code, so it took a week to cover these differences in migration path.
How does Tester compare to PHPUnit?
Trends Revealed
In the last post we looked at absolute downloads and trends of 3 PHP unit test frameworks:
Putting numbers and trends aside - this is about your needs. Do you need to change from Doctrine to Eloquent? From Symfony 4.2 to Laravel 5.8? From Symfony to Nette? Go for it, Rector will help you with the boring PHP work you'd love to skip.
The guy in the pub that night needed this, so... challenge accepted!
Single Test Case
Luckily, Tester and PHPUnit are like twins:
- share the same approach in configuring tests -
setUp&tearDown - do assert with a call -
Assert::true($value)vs.self::assertTrue($value) - do share naming -
public function testSomething() - do share data providers -
@dataProvider
So all we need to do is rename a few methods? There are still a few gotchas:
Assert::exception()uses code inside anonymous function, while in PHPUnit it's just above the code that should fail- Tester includes bootstrap itself, PHPUnit include in
phpunit.xml - Tester creates the test under the test
(new SomeTest())->run(), PHPUnit creates them automatically
Luckily, last 2 operations are subtractions, so we can just remove them.
And the Result?
<?php
namespace App\Tests;
use App\Entity\SomeObject;
-use Tester\Assert;
-use Tester\TestCase;
-require_once __DIR__ . '/bootstrap.php';
-class ExpensiveObjectTest extends TestCase
+class ExpensiveObjectTest extends \PHPUnit\Framework\TestCase
{
public function testSwitches()
{
- Assert::false(5);
+ $this->assertFalse(5);
- Assert::falsey('value', 'some message');
+ $this->assertFalse((bool) 'value', 'some message');
- Assert::truthy(true);
+ $this->assertTrue(true);
}
public function testTypes()
{
$value = 'x';
- Assert::type('array', $value);
+ $this->assertIsArray($value);
- Assert::type(SomeObject::class, $value);
+ $this->assertInstanceOf(SomeObject::class, $value);
}
public function testException()
{
$someObject = new SomeObject;
- Assert::exception(function () use ($someObject) {
- $someObject->setPrice('twenty dollars');
- }, InvalidArgumentException::class, 'Price should be string, you know');
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('Price should be string, you know');
+ $someObject->setPrice('twenty dollars');
}
+ /**
+ * @doesNotPerformAssertions
+ */
public function testNoError()
{
- Assert::noError(function () {
- new SomeObject(25)
- });
+ new SomeObject(25);
}
}
-(new ExpensiveObjectTest())->run();
How to Instantly Migrate from Nette\Tester to PHPUnit?
- Install Rector
composer require rector/rector --dev
- Add
rector.phpconfig:
use Rector\Set\ValueObject\SetList;
use Rector\Config\RectorConfig;
return function (RectorConfig $rectorConfig): void {
$rectorConfig->import(SetList::NETTE_TESTER_TO_PHPUNIT);
};
- Run it on
/testsdirectory
vendor/bin/rector process tests
Then take few minutes to polish the details that Rectors missed and send the PR to your project ✅
Happy coding!