Skip to content

Commit 2332593

Browse files
committed
Deprecate ControllerTester
1 parent fb54406 commit 2332593

8 files changed

Lines changed: 330 additions & 11 deletions

File tree

phpunit.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<file>./system/ComposerScripts.php</file>
2727
<file>./system/Config/Routes.php</file>
2828
<file>./system/Test/bootstrap.php</file>
29+
<file>./system/Test/ControllerTester.php</file>
2930
<file>./system/Test/FeatureTestCase.php</file>
3031
</exclude>
3132

system/Test/ControllerResponse.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
/**
1818
* Testable response from a controller
19+
*
20+
* @deprecated Use TestResponse directly
1921
*/
2022
class ControllerResponse extends TestResponse
2123
{
@@ -38,7 +40,7 @@ class ControllerResponse extends TestResponse
3840
protected $dom;
3941

4042
/**
41-
* Maintains the deprecated $domParser property.
43+
* Maintains the deprecated $dom property.
4244
*/
4345
public function __construct()
4446
{
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the CodeIgniter 4 framework.
5+
*
6+
* (c) CodeIgniter Foundation <admin@codeigniter.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace CodeIgniter\Test;
13+
14+
use CodeIgniter\Controller;
15+
use CodeIgniter\HTTP\IncomingRequest;
16+
use CodeIgniter\HTTP\Response;
17+
use CodeIgniter\HTTP\URI;
18+
use Config\App;
19+
use Config\Services;
20+
use InvalidArgumentException;
21+
use Throwable;
22+
23+
/**
24+
* Controller Test Trait
25+
*
26+
* Provides features that make testing controllers simple and fluent.
27+
*
28+
* Example:
29+
*
30+
* $this->withRequest($request)
31+
* ->withResponse($response)
32+
* ->withURI($uri)
33+
* ->withBody($body)
34+
* ->controller('App\Controllers\Home')
35+
* ->execute('methodName');
36+
*/
37+
trait ControllerTestTrait
38+
{
39+
/**
40+
* Controller configuration.
41+
*
42+
* @var App
43+
*/
44+
protected $appConfig;
45+
46+
/**
47+
* Request.
48+
*
49+
* @var IncomingRequest
50+
*/
51+
protected $request;
52+
53+
/**
54+
* Response.
55+
*
56+
* @var Response
57+
*/
58+
protected $response;
59+
60+
/**
61+
* Message logger.
62+
*
63+
* @var LoggerInterface
64+
*/
65+
protected $logger;
66+
67+
/**
68+
* Initialized controller.
69+
*
70+
* @var Controller
71+
*/
72+
protected $controller;
73+
74+
/**
75+
* URI of this request.
76+
*
77+
* @var string
78+
*/
79+
protected $uri = 'http://example.com';
80+
81+
/**
82+
* Request or response body.
83+
*
84+
* @var string|null
85+
*/
86+
protected $body;
87+
88+
/**
89+
* Initializes required components.
90+
*/
91+
protected function setUpControllerTester(): void
92+
{
93+
if (empty($this->appConfig))
94+
{
95+
$this->appConfig = config('App');
96+
}
97+
98+
if (! $this->uri instanceof URI)
99+
{
100+
$this->uri = Services::uri($this->appConfig->baseURL ?? 'http://example.com/', false);
101+
}
102+
103+
if (empty($this->request))
104+
{
105+
// Do some acrobatics so we can use the Request service with our own URI
106+
$tempUri = Services::uri();
107+
Services::injectMock('uri', $this->uri);
108+
109+
$this->withRequest(Services::request($this->appConfig, false)->setBody($this->body));
110+
111+
// Restore the URI service
112+
Services::injectMock('uri', $tempUri);
113+
}
114+
115+
if (empty($this->response))
116+
{
117+
$this->response = Services::response($this->appConfig, false);
118+
}
119+
120+
if (empty($this->logger))
121+
{
122+
$this->logger = Services::logger();
123+
}
124+
}
125+
126+
/**
127+
* Loads the specified controller, and generates any needed dependencies.
128+
*
129+
* @param string $name
130+
*
131+
* @return mixed
132+
*/
133+
public function controller(string $name)
134+
{
135+
if (! class_exists($name))
136+
{
137+
throw new InvalidArgumentException('Invalid Controller: ' . $name);
138+
}
139+
140+
$this->controller = new $name();
141+
$this->controller->initController($this->request, $this->response, $this->logger);
142+
143+
return $this;
144+
}
145+
146+
/**
147+
* Runs the specified method on the controller and returns the results.
148+
*
149+
* @param string $method
150+
* @param array $params
151+
*
152+
* @throws InvalidArgumentException
153+
*
154+
* @return TestResponse
155+
*/
156+
public function execute(string $method, ...$params)
157+
{
158+
if (! method_exists($this->controller, $method) || ! is_callable([$this->controller, $method]))
159+
{
160+
throw new InvalidArgumentException('Method does not exist or is not callable in controller: ' . $method);
161+
}
162+
163+
// The URL helper is always loaded by the system
164+
// so ensure it's available.
165+
helper('url');
166+
167+
$result = (new TestResponse())
168+
->setRequest($this->request)
169+
->setResponse($this->response);
170+
171+
$response = null;
172+
try
173+
{
174+
ob_start();
175+
176+
$response = $this->controller->{$method}(...$params);
177+
}
178+
catch (Throwable $e)
179+
{
180+
$code = $e->getCode();
181+
182+
// If code is not a valid HTTP status then assume there is an error
183+
if ($code < 100 || $code >= 600)
184+
{
185+
throw $e;
186+
}
187+
188+
$result->response()->setStatusCode($code);
189+
}
190+
finally
191+
{
192+
$output = ob_get_clean();
193+
194+
// If the controller returned a response, use it
195+
if (isset($response) && $response instanceof Response)
196+
{
197+
$result->setResponse($response);
198+
}
199+
200+
// check if controller returned a view rather than echoing it
201+
if (is_string($response))
202+
{
203+
$output = $response;
204+
$result->response()->setBody($output);
205+
$result->setBody($output);
206+
}
207+
elseif (! empty($response) && ! empty($response->getBody()))
208+
{
209+
$result->setBody($response->getBody());
210+
}
211+
else
212+
{
213+
$result->setBody('');
214+
}
215+
}
216+
217+
// If not response code has been sent, assume a success
218+
if (empty($result->response()->getStatusCode()))
219+
{
220+
$result->response()->setStatusCode(200);
221+
}
222+
223+
return $result;
224+
}
225+
226+
/**
227+
* Set controller's config, with method chaining.
228+
*
229+
* @param mixed $appConfig
230+
*
231+
* @return mixed
232+
*/
233+
public function withConfig($appConfig)
234+
{
235+
$this->appConfig = $appConfig;
236+
237+
return $this;
238+
}
239+
240+
/**
241+
* Set controller's request, with method chaining.
242+
*
243+
* @param mixed $request
244+
*
245+
* @return mixed
246+
*/
247+
public function withRequest($request)
248+
{
249+
$this->request = $request;
250+
251+
// Make sure it's available for other classes
252+
Services::injectMock('request', $request);
253+
254+
return $this;
255+
}
256+
257+
/**
258+
* Set controller's response, with method chaining.
259+
*
260+
* @param mixed $response
261+
*
262+
* @return mixed
263+
*/
264+
public function withResponse($response)
265+
{
266+
$this->response = $response;
267+
268+
return $this;
269+
}
270+
271+
/**
272+
* Set controller's logger, with method chaining.
273+
*
274+
* @param mixed $logger
275+
*
276+
* @return mixed
277+
*/
278+
public function withLogger($logger)
279+
{
280+
$this->logger = $logger;
281+
282+
return $this;
283+
}
284+
285+
/**
286+
* Set the controller's URI, with method chaining.
287+
*
288+
* @param string $uri
289+
*
290+
* @return mixed
291+
*/
292+
public function withUri(string $uri)
293+
{
294+
$this->uri = new URI($uri);
295+
296+
return $this;
297+
}
298+
299+
/**
300+
* Set the method's body, with method chaining.
301+
*
302+
* @param string|null $body
303+
*
304+
* @return mixed
305+
*/
306+
public function withBody($body)
307+
{
308+
$this->body = $body;
309+
310+
return $this;
311+
}
312+
}

system/Test/ControllerTester.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
* ->withBody($body)
3434
* ->controller('App\Controllers\Home')
3535
* ->execute('methodName');
36+
*
37+
* @deprecated Use ControllerTestTrait instead
3638
*/
3739
trait ControllerTester
3840
{

tests/system/Test/ControllerTesterTest.php renamed to tests/system/Test/ControllerTestTraitTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
* @runTestsInSeparateProcesses
1616
* @preserveGlobalState disabled
1717
*/
18-
class ControllerTesterTest extends CIUnitTestCase
18+
class ControllerTestTraitTest extends CIUnitTestCase
1919
{
20-
use ControllerTester;
20+
use ControllerTestTrait;
2121

2222
public function testBadController()
2323
{

tests/system/Test/FeatureTestTraitTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* @runTestsInSeparateProcesses
1414
* @preserveGlobalState disabled
1515
*/
16-
class FeatureTestCaseTest extends CIUnitTestCase
16+
class FeatureTestTraitTest extends CIUnitTestCase
1717
{
1818
use FeatureTestTrait;
1919

user_guide_src/source/changelogs/v4.1.2.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ Deprecations:
3333
- Deprecated ``Security::isExpired()`` to use the ``Cookie``'s internal expires status.
3434
- Deprecated ``CIDatabaseTestCase`` to use the ``DatabaseTestTrait`` instead.
3535
- Deprecated ``FeatureTestCase`` to use the ``FeatureTestTrait`` instead.
36+
- Deprecated ``ControllerTester`` to use the ``ControllerTestTrait`` instead.
37+
- Consolidated and deprecated ``ControllerResponse`` and ``FeatureResponse`` in favor of ``TestResponse``.
3638

3739
Bugs Fixed:
3840

0 commit comments

Comments
 (0)