Running Superdesk Publisher Tests¶
The Superdesk Publisher project uses a third-party service which automatically runs tests for any submitted patch. If the new code breaks any test, the pull request will show an error message with a link to the full error details.
In any case, it’s a good practice to run tests locally before submitting a patch for inclusion, to check that you have not broken anything.
Before Running the Tests¶
To run the Superdesk Publisher test suite, install the external dependencies used during the tests, such as Doctrine, Twig and Monolog. To do so, install Composer and execute the following:
1 | composer install
|
Note
For unit tests we use PHPSpec, for functional tests PHPUnit and Behat for integration.
Running the PHPUnit Tests¶
Then, run the test suite from the Superdesk Publisher root directory with the following command:
1 | bin/phpunit -c app/
|
The output should display OK
. If not, read the reported errors to figure out
what’s going on and if the tests are broken because of the new code.
Tip
The entire Superdesk Publisher suite can take up to several minutes to complete. If you
want to test a single component/bundle, type its path after the phpunit
command,
e.g.:
1 | bin/phpunit src/SWP/Bundle/MultiTenancyBundle/
|
Running the PHPSpec specs¶
Note
This section is based on Sylius documentation.
PHPSpec is a PHP toolset to drive emergent design by specification. It is not really a testing tool, but a design instrument, which helps structuring the objects and how they work together.
The Superdesk Publisher approach is to always describe the behaviour of the next object you are about to implement.
As an example, we’ll write a service, which sets the current tenant in the context.
To initialize a new spec, use the desc
command.
We just need to tell PHPSpec we will be working on the TenantContext class.
1 2 | bin/phpspec desc "SWP\Component\MultiTenancy\Context\TenantContext"
Specification for TenantContext created in spec.
|
What have we just done? PHPSpec has created the spec for us. You can navigate to the spec folder and see the spec there:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php
namespace spec\SWP\Component\MultiTenancy\Context;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TenantContextSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('SWP\Component\MultiTenancy\Context\TenantContext');
}
}
|
The object behaviour is made of examples. Examples are encased in public methods,
started with it_
or its_
.
PHPSpec searches for such methods in your specification to run.
Why underscores for example names? just_because_its_much_easier_to_read
than someLongCamelCasingLikeThat
.
Now, let’s write the first example, which will set the current tenant:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?php
namespace spec\SWP\Component\MultiTenancy\Context;
use PhpSpec\ObjectBehavior;
use SWP\Component\MultiTenancy\Model\TenantInterface;
class TenantContextSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('SWP\Component\MultiTenancy\Context\TenantContext');
}
function it_should_set_tenant(TenantInterface $tenant)
{
$tenant->getId()->willReturn(1);
$tenant->getSubdomain()->willReturn('example1');
$tenant->getName()->willReturn('example1');
$this->setTenant($tenant)->shouldBeNull();
}
}
|
The example looks clear and simple, the TenantContext
service should obtain the tenant id, name, subdomain and call the method to set the tenant.
Try running the example by using the following command:
1 2 3 4 5 6 7 8 | bin/phpspec run
> spec\SWP\Component\MultiTenancy\Context\TenantContext
✘ it should set tenant
Class TenantContext does not exists.
Do you want me to create it for you? [Y/n]
|
Once the class is created and you run the command again, PHPSpec will ask if it should create the method as well. Start implementing the initial version of the TenantContext.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php
namespace SWP\Component\MultiTenancy\Context;
use SWP\Component\MultiTenancy\Model\TenantInterface;
/**
* Class TenantContext.
*/
class TenantContext implements TenantContextInterface
{
/**
* @var TenantInterface
*/
protected $tenant;
/**
* {@inheritdoc}
*/
public function setTenant(TenantInterface $tenant)
{
$this->tenant = $tenant;
}
}
|
Done! If you run PHPSpec again, you should see the following output:
1 2 3 4 5 6 7 8 | bin/phpspec run
> spec\SWP\Component\MultiTenancy\Context\TenantContext
✔ it should set tenant
1 examples (1 passed)
123ms
|
This example is greatly simplified, in order to illustrate how we work. More examples might cover errors, API exceptions and other edge-cases.
A few tips & rules to follow when working with PHPSpec & Superdesk Publisher:
- RED is good, add or fix the code to make it green;
- RED-GREEN-REFACTOR is our rule;
- All specs must pass;
- When writing examples, describe the behaviour of the object in the present tense;
- Omit the
public
keyword; - Use underscores (
_
) in the examples; - Use type hinting to mock and stub classes;
- If your specification is getting too complex, the design is wrong. Try decoupling a bit more;
- If you cannot describe something easily, probably you should not be doing it that way;
- shouldBeCalled or willReturn, never together, except for builders;
- Use constants in assumptions but strings in expected results;