Resource icon

Unit testing XenForo addons - tutorial

v3.0.0 of the Unit Test Framework has been released with compatibility for XF 2.3. Updates are via Composer.

XenForo 2.1 addons should use Unit Test Framework v1.x
XenForo 2.2 addons should use Unit Test Framework v2.x
XenForo 2.3 addons should use Unit Test Framework v3.x

Changes:
  • update Job manager to work with changes for XF 2.3 - particularly the new JobParams class
  • update mail handling to work with Symfony mail
  • remove mail queue support - we now simply disable queueing in the config to force all emails to be sent via our test transport
v2.1.0 of the Unit Test Framework has been released. Updates are via Composer.

Note breaking changes:

Testing helper function isolateAddon has been removed in favour of a new variable set in TestCase.php

When updating to the newest version of the framework, you'll need to remove any references to isolateAddon and ensure that the updated versions of TestCase.php and CreatesApplication.php are copied across from the updated package.

Specifically, the following lines:

TestCase.php
PHP:
protected $addonsToLoad = [];

CreatesApplication.php
PHP:
$options['xf-addons'] = $this->addonsToLoad ?: [];

return \XF::setupApp('Hampel\Testing\App', $options);

Please read the documentation - in particular section 9 on "Configuring the Framework" for more information.
XenForo 2.2 implements Swiftmail 6, which changes some of the method/interface signatures - so I've had to push a new major release for XF 2.2 compatibility.

XenForo 2.1 addons should use Unit Test Framework v1.x
XenForo 2.2 addons should use Unit Test Framework v2.x
  • bugfix: fixed typo in function name assertSimpleCacheEqual => assertSimpleCacheEquals
  • bugfix: close the database connection on tearDown to avoid connection limit issues (unless it's been mocked)
  • Like
Reactions: Earl
  • Feature: added new functionality to Interacts with Container
    • mockService
  • Feature: Interacts with Http, new function:
    • fakesHttp

mockService
Mock a service factory builder in the container.

Parameters
  • shortName - the short name of the service class to be mocked
  • mock - optional - the mock closure to define expectations on
Example:
PHP:
<?php namespace Tests\Unit;

use Tests\TestCase;
use XF\Http\Request;

class ServiceTest extends TestCase
{
    public function test_service()
    {       
        // mock our service class
        $this->mockService('XF:User\EmailStop', function ($mock) {
            $mock->expects()->stop('list')->once();
        });

        // execute some test code which causes the mocked code to be executed, for example
        $emailStop = $this->app()->service('XF:User\EmailStop');
        $emailStop->stop('list');
    }
}


fakesHttp
Allow us to assert that certain HTTP requests were (or were not) sent as a result of executing our test code, and to supply mock HTTP responses without side-effects (ie no requests actually sent).

This function relies on the Mock Handler and History Middleware provided by the Guzzle HTTP library used by XenForo.

Refer to the Guzzle documentation Testing Guzzle Clients for more information on how the Mock Handler and History Middleware works.

Parameters:
  • array responseStack - an array of Psr7 Responses or Request Exceptions to return - one for each request made
  • bool untrusted - set to true when using the untrusted client in XenForo
Assertions available:
  • assertHttpRequestSent
  • assertHttpRequestSentTimes
  • assertHttpRequestNotSent
  • assertNoHttpRequestSent
Example:
PHP:
<?php namespace Tests\Unit;

use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Exception\RequestException;
use Tests\TestCase;

class HttpTest extends TestCase
{
    public function test_http()
    {   
        // tell Guzzle not to send requests, but to instead return our mock responses, one for each
        // request that we make
        $this->fakesHttp([
            new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'),
            new Response(202, ['Content-Length' => 0]),
            new RequestException('Error Communicating with Server', new Request('GET', 'test'))
        ]);

        // our third response generates an exception
        $this->expectException(RequestException::class);

        // execute some code which sends an Http request
        $response1 = $this->app()->http()->client()->get('/');
        $response2 = $this->app()->http()->client()->get('/foo');
        $response3 = $this->app()->http()->client()->get('/bar');

        // assert something about the requests that were sent
        $this->assertHttpRequestSent(function ($request) {
            return strval($request->getUri()) == '/' OR strval($request->getUri()) == '/foo';
        });

        // assert something about our responses
        $this->assertEquals(200, $response1->getStatusCode());
        $this->assertEquals(202, $response2->getStatusCode());
    }
}
  • Feature: added new functionality to Interacts with Extension
    • isolateAddon
  • Feature: Interacts with Registry - adds:
    • fakesRegistry
  • Feature: Interacts with Filesystem - adds:
    • swapFs
    • mockFs
  • bugfix: after mocking the database, set up the entity manager again, so we get the mocked database
  • bugfix: should pass options array through to parent
  • bugfix: cleaned up function visibility for consistency
  • bugfix: override protected function preLoadData so we can call it directly when faking the registry
Back
Top Bottom