Skip to content

Commit d0c6277

Browse files
committed
Provide test functionality in traits
Fix #202. separate http client from trait Make property and methods private Add traits to docs Reduce levels of inheritance
1 parent 6c755b3 commit d0c6277

14 files changed

+936
-654
lines changed

doc/includes/symfony-process.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Requires Symfony’s Process component, so make sure to include that in your
2+
project:
3+
4+
.. code-block:: bash
5+
6+
$ composer require symfony/process

doc/proxy-clients.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ Then pass that adapter to the caching proxy client::
6666
$proxyClient = new Varnish($servers, '/baseUrl', $adapter);
6767
// Varnish as example, but also possible for NGINX and Symfony
6868

69+
.. _varnish client:
70+
6971
Varnish Client
7072
~~~~~~~~~~~~~~
7173

doc/symfony-cache-configuration.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,34 @@ the ``session_name_prefix`` option) in the requests to the backend. If you need
179179
a different behavior, overwrite ``UserContextSubscriber::cleanupHashLookupRequest``
180180
with your own logic.
181181

182+
.. _symfony-cache x-debugging:
183+
184+
Debugging
185+
~~~~~~~~~
186+
187+
For the ``assertHit`` and ``assertMiss`` assertions to work, you need to add
188+
debug information in your AppCache. Create the cache kernel with the option
189+
``'debug' => true`` and add the following to your ``AppCache``::
190+
191+
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
192+
{
193+
$response = parent::handle($request, $type, $catch);
194+
195+
if ($response->headers->has('X-Symfony-Cache')) {
196+
if (false !== strpos($response->headers->get('X-Symfony-Cache'), 'miss')) {
197+
$state = 'MISS';
198+
} elseif (false !== strpos($response->headers->get('X-Symfony-Cache'), 'fresh')) {
199+
$state = 'HIT';
200+
} else {
201+
$state = 'UNDETERMINED';
202+
}
203+
$response->headers->set('X-Cache', $state);
204+
}
205+
206+
return $response;
207+
}
208+
209+
The ``UNDETERMINED`` state should never happen. If it does, it means that your
210+
HttpCache is not correctly set into debug mode.
211+
182212
.. _HttpCache: http://symfony.com/doc/current/book/http_cache.html#symfony-reverse-proxy

doc/testing-your-application.rst

Lines changed: 136 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,30 @@ Testing Your Application
44
========================
55

66
This chapter describes how to test your application against your reverse proxy.
7-
8-
The FOSHttpCache library provides base test case classes to help you write
9-
functional tests. This is helpful to test the way your application sets caching
10-
headers and invalidates cached content.
11-
12-
By having your test classes extend one of the test case classes, you get:
13-
14-
* independent tests: all previously cached content is removed in the tests
15-
``setUp`` method. The way this is done depends on which reverse proxy you use;
16-
* an instance of this library’s client that is configured to talk to your
17-
reverse proxy server. See reverse proxy specific sections for details;
18-
* convenience methods for executing HTTP requests to your application:
19-
``$this->getHttpAdapter()`` and ``$this->getResponse()``;
20-
* custom assertions ``assertHit`` and ``assertMiss`` for validating a cache
7+
By running your tests against a live instance of your caching proxy, you can
8+
validate the caching headers that your application sets, and the invalidation
9+
rules that it defines.
10+
11+
The FOSHttpCache library provides traits and base test classes to help you write
12+
functional tests. Using the traits, you can extend your own (or your
13+
framework’s) base test classes. For convenience, you can also extend the
14+
FOSHttpCache base test classes, as they include a sensible set of traits by
15+
default.
16+
17+
By using the traits, you get:
18+
19+
* independent tests: all previously cached content is removed in the test’s
20+
``setUp()`` method;
21+
* an instance of this library’s proxy client that is configured to talk to your
22+
proxy server for invalidation requests;
23+
* a convenience method for executing HTTP requests to your caching proxy:
24+
``$this->getResponse()``;
25+
* custom assertions ``assertHit()`` and ``assertMiss()`` for validating a cache
2126
hit/miss.
2227

28+
Configuration
29+
-------------
30+
2331
The recommended way to configure the test case is by setting constants
2432
in your ``phpunit.xml``. Alternatively, you can override the getter methods.
2533

@@ -85,89 +93,69 @@ You can override getters in your test class in the following way::
8593
}
8694
}
8795

88-
VarnishTestCase
89-
---------------
96+
Traits
97+
------
9098

91-
Configuration
92-
'''''''''''''
99+
Caching Proxy Server Traits
100+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
93101

94-
By default, the ``VarnishTestCase`` starts and stops a Varnish server for you.
95-
Make sure ``symfony/process`` is available in your project:
102+
FOSHttpCache provides three caching proxy traits that:
96103

97-
.. code-block:: bash
104+
* if necessary, start your caching proxy server before running the tests;
105+
* clear any cached content between tests to guarantee test isolation;
106+
* if necessary, stop the caching proxy server after the tests have finished;
107+
* provide ``getProxyClient()``, which returns the right
108+
:doc:`proxy client <proxy-clients>` for your proxy server.
98109

99-
$ composer require symfony/process
110+
You only need to include one of these traits in your test classes. Which one
111+
you need (``VarnishTest``, ``NginxTest`` or ``SymfonyTest``) depends on the
112+
caching proxy server that you use.
100113

101-
Then set your Varnish configuration (VCL) file. All available configuration
102-
parameters are shown below.
114+
VarnishTest Trait
115+
"""""""""""""""""
116+
117+
.. include:: includes/symfony-process.rst
118+
119+
Then configure the following parameters. The web server hostname and path to
120+
your VCL file are required.
103121

104122
======================= ========================= ================================================== ===========================================
105123
Constant Getter Default Description
106124
======================= ========================= ================================================== ===========================================
125+
``WEB_SERVER_HOSTNAME`` ``getHostName()`` hostname your application can be reached at
107126
``VARNISH_FILE`` ``getConfigFile()`` your Varnish configuration (VCL) file
108127
``VARNISH_BINARY`` ``getBinary()`` ``varnishd`` your Varnish binary
109128
``VARNISH_PORT`` ``getCachingProxyPort()`` ``6181`` port Varnish listens on
110129
``VARNISH_MGMT_PORT`` ``getVarnishMgmtPort()`` ``6182`` Varnish management port
111130
``VARNISH_CACHE_DIR`` ``getCacheDir()`` ``sys_get_temp_dir()`` + ``/foshttpcache-varnish`` directory to use for cache
112131
``VARNISH_VERSION`` ``getVarnishVersion()`` ``4`` installed varnish application version
113-
``WEB_SERVER_HOSTNAME`` ``getHostName()`` hostname your application can be reached at
114132
======================= ========================= ================================================== ===========================================
115133

116-
Enable Assertions
117-
'''''''''''''''''
118-
119-
For the `assertHit` and `assertMiss` assertions to work, you need to add a
120-
:ref:`custom X-Cache header <varnish_debugging>` to responses served
121-
by your Varnish.
122-
123-
NginxTestCase
124-
-------------
125-
126-
Configuration
127-
'''''''''''''
128-
129-
By default, the ``NginxTestCase`` starts and stops the NGINX server for you and
130-
deletes all cached contents. Make sure ``symfony/process`` is available in your
131-
project:
134+
NginxTest Trait
135+
"""""""""""""""
132136

133-
.. code-block:: bash
137+
.. include:: includes/symfony-process.rst
134138

135-
$ composer require symfony/process
136-
137-
You have to set your NGINX configuration file. All available configuration
138-
parameters are shown below.
139+
Then configure the following parameters. The web server hostname and path to
140+
your NGINX configuration file are required.
139141

140142
======================= ========================= ================================================ ===========================================
141143
Constant Getter Default Description
142144
======================= ========================= ================================================ ===========================================
145+
``WEB_SERVER_HOSTNAME`` ``getHostName()`` hostname your application can be reached at
143146
``NGINX_FILE`` ``getConfigFile()`` your NGINX configuration file
144147
``NGINX_BINARY`` ``getBinary()`` ``nginx`` your NGINX binary
145148
``NGINX_PORT`` ``getCachingProxyPort()`` ``8088`` port NGINX listens on
146149
``NGINX_CACHE_PATH`` ``getCacheDir()`` ``sys_get_temp_dir()`` + ``/foshttpcache-nginx`` directory to use for cache
147150
Must match `proxy_cache_path` directive in
148151
your configuration file.
149-
``WEB_SERVER_HOSTNAME`` ``getHostName()`` hostname your application can be reached at
150152
======================= ========================= ================================================ ===========================================
151153

152-
Enable Assertions
153-
'''''''''''''''''
154+
SymfonyTest Trait
155+
"""""""""""""""""
154156

155-
For the `assertHit` and `assertMiss` assertions to work, you need to add a
156-
:ref:`custom X-Cache header <nginx_debugging>` to responses served
157-
by your Nginx.
158-
159-
SymfonyTestCase
160-
---------------
161-
162-
This test case helps to test invalidation requests with a symfony application
163-
running the Symfony HttpCache and invalidating its cache folder to get reliable
164-
tests.
165-
166-
The ``SymfonyTestCase`` does automatically start a web server. It is assumed
167-
that the web server you run for the application has the HttpCache integrated.
168-
169-
Configuration
170-
'''''''''''''
157+
It is assumed that the web server you run for the application has the HttpCache
158+
integrated.
171159

172160
======================= ========================= ================================================ ===========================================
173161
Constant Getter Default Description
@@ -180,59 +168,92 @@ Constant Getter Default
180168
running PHPUnit.
181169
======================= ========================= ================================================ ===========================================
182170

183-
Enable Assertions
184-
'''''''''''''''''
171+
HttpCaller Trait
172+
~~~~~~~~~~~~~~~~
173+
174+
Provides your tests with a ``getResponse`` method, which retrieves a URI from
175+
your application through a real HTTP call that goes through the HTTP caching
176+
proxy::
185177

186-
For the `assertHit` and `assertMiss` assertions to work, you need to add debug
187-
information in your AppCache. Create the cache kernel with the option
188-
``'debug' => true`` and add the following to your ``AppCache``::
178+
use FOS\HttpCache\Test\HttpCaller;
189179

190-
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
180+
class YourTest extends \PHPUnit_Framework_TestCase
191181
{
192-
$response = parent::handle($request, $type, $catch);
193-
194-
if ($response->headers->has('X-Symfony-Cache')) {
195-
if (false !== strpos($response->headers->get('X-Symfony-Cache'), 'miss')) {
196-
$state = 'MISS';
197-
} elseif (false !== strpos($response->headers->get('X-Symfony-Cache'), 'fresh')) {
198-
$state = 'HIT';
199-
} else {
200-
$state = 'UNDETERMINED';
201-
}
202-
$response->headers->set('X-Cache', $state);
182+
public function testCachingHeaders()
183+
{
184+
// Get some response from your application
185+
$response = $this->getResponse('/path');
186+
187+
// Optionally with request headers and a custom method
188+
$response = $this->getResponse('/path', ['Accept' => 'text/json'], 'PUT');
203189
}
190+
}
191+
192+
CacheAssertions Trait
193+
~~~~~~~~~~~~~~~~~~~~~
194+
195+
Provides cache hit/miss assertions to your tests. To enable the these
196+
``assertHit`` and ``assertMiss`` assertions, you need to configure your caching
197+
server to set an `X-Cache` header with the cache status:
204198

205-
return $response;
199+
* :ref:`Varnish <varnish_debugging>`
200+
* :ref:`NGINX <nginx_debugging>`
201+
* :ref:`Symfony HttpCache <symfony-cache x-debugging>`
202+
203+
Then use the assertions as follows::
204+
205+
use FOS\HttpCache\Test\CacheAssertions;
206+
207+
class YourTest extends \PHPUnit_Framework_TestCase
208+
{
209+
public function testCacheHitOrMiss()
210+
{
211+
// Assert the application response is a cache miss
212+
$this->assertMiss($response);
213+
214+
// Or assert it is a hit
215+
$this->assertHit($response);
216+
}
206217
}
207218

208-
The ``UNDETERMINED`` state should never happen. If it does, it means that your
209-
HttpCache is not correctly set into debug mode.
219+
Base Classes for Convenience
220+
----------------------------
221+
222+
If you prefer, you can extend your test classes from ``VarnishTestCase``,
223+
``NginxTestCase`` or ``SymfonyTestCase``. The appropriate traits will then
224+
automatically be included.
210225

211226
Usage
212227
-----
213228

214229
This example shows how you can test whether the caching headers your
215-
application sets influence Varnish as you expect them to::
230+
application sets influence your caching proxy as you expect them to::
216231

217-
use FOS\HttpCache\Test\VarnishTestCase;
232+
use FOS\HttpCache\Test\CacheAssertions;
233+
use FOS\HttpCache\Test\HttpCaller;
234+
use FOS\HttpCache\Test\VarnishTest;
235+
// or FOS\HttpCache\Test\NginxTest;
236+
// or FOS\HttpCache\Test\SymfonyTest;
218237

219-
class YourFunctionalTest extends VarnishTestCase
238+
class YourTest extends \PHPUnit_Framework_TestCase
220239
{
221240
public function testCachingHeaders()
222241
{
223-
// Varnish is restarted, so you don’t have to worry about previously
224-
// cached content. Before continuing, the VarnishTestCase waits for
225-
// Varnish to become available.
242+
// The caching proxy is (re)started, so you don’t have to worry
243+
// about previously cached content. Before continuing, the
244+
// VarnishTest/ NginxTest trait waits for the caching proxy to
245+
// become available.
226246

227-
// Retrieve an URL from your application
247+
// Retrieve a URL from your application
228248
$response = $this->getResponse('/your/resource');
229249

230250
// Assert the response was a cache miss (came from the backend
231251
// application)
232252
$this->assertMiss($response);
233253

234-
// Assume the URL /your/resource sets caching headers. If we retrieve
235-
// it again, we should have a cache hit (response delivered by Varnish):
254+
// Assume the URL /your/resource sets caching headers. If we
255+
// retrieve it again, we should have a cache hit (response delivered
256+
// by the caching proxy):
236257
$response = $this->getResponse('/your/resource');
237258
$this->assertHit($response);
238259
}
@@ -241,26 +262,34 @@ application sets influence Varnish as you expect them to::
241262
This example shows how you can test whether your application purges content
242263
correctly::
243264

244-
public function testCachePurge()
265+
use FOS\HttpCache\Test\CacheAssertions;
266+
use FOS\HttpCache\Test\HttpCaller;
267+
use FOS\HttpCache\Test\VarnishTest;
268+
// or FOS\HttpCache\Test\NginxTest;
269+
// or FOS\HttpCache\Test\SymfonyTest;
270+
271+
class YourTest extends \PHPUnit_Framework_TestCase
245272
{
246-
// Again, Varnish is restarted, so your test is independent from
247-
// other tests
273+
public function testCachePurge()
274+
{
275+
// Again, the caching proxy is restarted, so your test is independent
276+
// from other tests
248277

249-
$url = '/blog/articles/1';
278+
$url = '/blog/articles/1';
250279

251-
// First request must be a cache miss
252-
$this->assertMiss($this->getResponse($url));
280+
// First request must be a cache miss
281+
$this->assertMiss($this->getResponse($url));
253282

254-
// Next requests must be a hit
255-
$this->assertHit($this->getResponse($url));
283+
// Next requests must be a hit
284+
$this->assertHit($this->getResponse($url));
256285

257-
// Purge
258-
$this->varnish->purge('/blog/articles/1');
286+
// Purge
287+
$this->getProxyClient()->purge('/blog/articles/1');
259288

260-
// First request after must again be a miss
261-
$this->assertMiss($this->getResponse($url));
289+
// First request after must again be a miss
290+
$this->assertMiss($this->getResponse($url));
291+
}
262292
}
263293

264-
Tests for Nginx look the same but extend the NginxTestCase.
265294
For more ideas, see this library’s functional tests in the
266295
:source:`tests/Functional/` directory.

0 commit comments

Comments
 (0)