I personally do it the same way you do right now. It takes some time to write but these kind of tests are still understandable next year. Earlier I always tried to write as less code as possible by making all kind of 'smart' methods but in the end it became all more complex especially when you later had to add a new test case.
You should make the test as simple as possible with a clear description.
But....
if you have a lot of repetitive tests you have an option to use data providers. A simple example would be:
/**
* @test
* @dataProvider emailProvider
*/
public function only_valid_email_addresses_should_be_allowed($email, $expected)
{
$this->assertEquas($expected, is_valid_email($email));
}
public function emailProvider()
{
// this data provider will call the test method 3 times with 2 parameters. You can optionally give the key a name.
return [
'optional name' => ['[email protected]', true],
'optional name 2' =>['johndoe.com', false],
'optional name 3' =>['[email protected]', true],
];
}
And maybe you can imagine that you could also turn this in something like:
/**
* @test
* @dataProvider permissionProvider
*/
public function owner_add_user_permission_test($expected, $projectStatus)
{
// setup the project here with the given $projectStatus and test if you can add a user and if it matches your expectation
}
public function permissionProvider()
{
return [
'add user to in progress project should be ok' => [true, 'in progress'],
'add user to draft project should be ok' =>[true, 'draft'],
'add user to closed project fail' =>[false, 'closed'],
];
}
This way you could simplify test by writing less code. But it can get messy and unreadable when you do this a lot and especially if you have more than 2 parameters. But it all depends to the thing your are trying to test
See also the documentation about PHPUnit dataproviders https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers