2
0

Zend_Test-PHPUnit-Examples.xml 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <sect2 id="zend.test.phpunit.examples">
  4. <title>Examples</title>
  5. <para>
  6. Knowing how to setup your testing infrastructure and how to make
  7. assertions is only half the battle; now it's time to start looking at
  8. some actual testing scenarios to see how you can leverage them.
  9. </para>
  10. <example id="zend.test.phpunit.examples.userController">
  11. <title>Testing a UserController</title>
  12. <para>
  13. Let's consider a standard task for a website: authenticating and registering users. In our example, we'll define a UserController for handling this, and have the following requirements:
  14. </para>
  15. <itemizedlist>
  16. <listitem><para>
  17. If a user is not authenticated, they will always be redirected
  18. to the login page of the controller, regardless of the action
  19. specified.
  20. </para></listitem>
  21. <listitem><para>
  22. The login form page will show both the login form and the
  23. registration form.
  24. </para></listitem>
  25. <listitem><para>
  26. Providing invalid credentials should result in returning to the
  27. login form.
  28. </para></listitem>
  29. <listitem><para>
  30. Valid credentials should result in redirecting to the user
  31. profile page.
  32. </para></listitem>
  33. <listitem><para>
  34. The profile page should be customized to contain the user's
  35. username.
  36. </para></listitem>
  37. <listitem><para>
  38. Authenticated users who visit the login page should be
  39. redirected to their profile page.
  40. </para></listitem>
  41. <listitem><para>
  42. On logout, a user should be redirected to the login page.
  43. </para></listitem>
  44. <listitem><para>
  45. With invalid data, registration should fail.
  46. </para></listitem>
  47. </itemizedlist>
  48. <para>
  49. We could, and should define further tests, but these will do for
  50. now.
  51. </para>
  52. <para>
  53. For our application, we will define a plugin, 'Initialize', that
  54. runs at <code>routeStartup()</code>. This allows us to encapsulate
  55. our bootstrap in an OOP interface, which also provides an easy way
  56. to provide a callback. Let's look at the basics of this class
  57. first:
  58. </para>
  59. <programlisting language="php"><![CDATA[
  60. class Bugapp_Plugin_Initialize extends Zend_Controller_Plugin_Abstract
  61. {
  62. /**
  63. * @var Zend_Config
  64. */
  65. protected static $_config;
  66. /**
  67. * @var string Current environment
  68. */
  69. protected $_env;
  70. /**
  71. * @var Zend_Controller_Front
  72. */
  73. protected $_front;
  74. /**
  75. * @var string Path to application root
  76. */
  77. protected $_root;
  78. /**
  79. * Constructor
  80. *
  81. * Initialize environment, root path, and configuration.
  82. *
  83. * @param string $env
  84. * @param string|null $root
  85. * @return void
  86. */
  87. public function __construct($env, $root = null)
  88. {
  89. $this->_setEnv($env);
  90. if (null === $root) {
  91. $root = realpath(dirname(__FILE__) . '/../../../');
  92. }
  93. $this->_root = $root;
  94. $this->initPhpConfig();
  95. $this->_front = Zend_Controller_Front::getInstance();
  96. }
  97. /**
  98. * Route startup
  99. *
  100. * @return void
  101. */
  102. public function routeStartup(Zend_Controller_Request_Abstract $request)
  103. {
  104. $this->initDb();
  105. $this->initHelpers();
  106. $this->initView();
  107. $this->initPlugins();
  108. $this->initRoutes();
  109. $this->initControllers();
  110. }
  111. // definition of methods would follow...
  112. }
  113. ]]></programlisting>
  114. <para>
  115. This allows us to create a bootstrap callback like the following:
  116. </para>
  117. <programlisting language="php"><![CDATA[
  118. class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
  119. {
  120. public function appBootstrap()
  121. {
  122. $controller = $this->getFrontController();
  123. $controller->registerPlugin(
  124. new Bugapp_Plugin_Initialize('development')
  125. );
  126. }
  127. public function setUp()
  128. {
  129. $this->bootstrap = array($this, 'appBootstrap');
  130. parent::setUp();
  131. }
  132. // ...
  133. }
  134. ]]></programlisting>
  135. <para>
  136. Once we have that in place, we can write our tests. However, what
  137. about those tests that require a user is logged in? The easy
  138. solution is to use our application logic to do so... and fudge a
  139. little by using the <code>resetRequest()</code> and
  140. <code>resetResponse()</code> methods, which will allow us to
  141. dispatch another request.
  142. </para>
  143. <programlisting language="php"><![CDATA[
  144. class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
  145. {
  146. // ...
  147. public function loginUser($user, $password)
  148. {
  149. $this->request->setMethod('POST')
  150. ->setPost(array(
  151. 'username' => $user,
  152. 'password' => $password,
  153. ));
  154. $this->dispatch('/user/login');
  155. $this->assertRedirectTo('/user/view');
  156. $this->resetRequest()
  157. ->resetResponse();
  158. $this->request->setPost(array());
  159. // ...
  160. }
  161. // ...
  162. }
  163. ]]></programlisting>
  164. <para>
  165. Now let's write tests:
  166. </para>
  167. <programlisting language="php"><![CDATA[
  168. class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
  169. {
  170. // ...
  171. public function testCallWithoutActionShouldPullFromIndexAction()
  172. {
  173. $this->dispatch('/user');
  174. $this->assertController('user');
  175. $this->assertAction('index');
  176. }
  177. public function testLoginFormShouldContainLoginAndRegistrationForms()
  178. {
  179. $this->dispatch('/user');
  180. $this->assertQueryCount('form', 2);
  181. }
  182. public function testInvalidCredentialsShouldResultInRedisplayOfLoginForm()
  183. {
  184. $request = $this->getRequest();
  185. $request->setMethod('POST')
  186. ->setPost(array(
  187. 'username' => 'bogus',
  188. 'password' => 'reallyReallyBogus',
  189. ));
  190. $this->dispatch('/user/login');
  191. $this->assertNotRedirect();
  192. $this->assertQuery('form');
  193. }
  194. public function testValidLoginShouldRedirectToProfilePage()
  195. {
  196. $this->loginUser('foobar', 'foobar');
  197. }
  198. public function testAuthenticatedUserShouldHaveCustomizedProfilePage()
  199. {
  200. $this->loginUser('foobar', 'foobar');
  201. $this->request->setMethod('GET');
  202. $this->dispatch('/user/view');
  203. $this->assertNotRedirect();
  204. $this->assertQueryContentContains('h2', 'foobar');
  205. }
  206. public function
  207. testAuthenticatedUsersShouldBeRedirectedToProfileWhenVisitingLogin()
  208. {
  209. $this->loginUser('foobar', 'foobar');
  210. $this->request->setMethod('GET');
  211. $this->dispatch('/user');
  212. $this->assertRedirectTo('/user/view');
  213. }
  214. public function testUserShouldRedirectToLoginPageOnLogout()
  215. {
  216. $this->loginUser('foobar', 'foobar');
  217. $this->request->setMethod('GET');
  218. $this->dispatch('/user/logout');
  219. $this->assertRedirectTo('/user');
  220. }
  221. public function testRegistrationShouldFailWithInvalidData()
  222. {
  223. $data = array(
  224. 'username' => 'This will not work',
  225. 'email' => 'this is an invalid email',
  226. 'password' => 'Th1s!s!nv@l1d',
  227. 'passwordVerification' => 'wrong!',
  228. );
  229. $request = $this->getRequest();
  230. $request->setMethod('POST')
  231. ->setPost($data);
  232. $this->dispatch('/user/register');
  233. $this->assertNotRedirect();
  234. $this->assertQuery('form .errors');
  235. }
  236. }
  237. ]]></programlisting>
  238. <para>
  239. Notice that these are terse, and, for the most part, don't look for
  240. actual content. Instead, they look for artifacts within the
  241. response -- response codes and headers, and DOM nodes. This allows
  242. you to verify that the structure is as expected -- preventing your
  243. tests from choking every time new content is added to the site.
  244. </para>
  245. <para>
  246. Also notice that we use the structure of the document in our tests.
  247. For instance, in the final test, we look for a form that has a node
  248. with the class of "errors"; this allows us to test merely for the
  249. presence of form validation errors, and not worry about what
  250. specific errors might have been thrown.
  251. </para>
  252. <para>
  253. This application <emphasis>may</emphasis> utilize a database. If
  254. so, you will probably need some scaffolding to ensure that the
  255. database is in a pristine, testable configuration at the beginning
  256. of each test. PHPUnit already provides functionality for doing so;
  257. <ulink
  258. url="http://www.phpunit.de/pocket_guide/3.3/en/database.html">read
  259. about it in the PHPUnit documentation</ulink>. We recommend
  260. using a separate database for testing versus production, and in
  261. particular recommend using either a SQLite file or in-memory
  262. database, as both options perform very well, do not require a
  263. separate server, and can utilize most SQL syntax.
  264. </para>
  265. </example>
  266. </sect2>
  267. <!--
  268. vim:se ts=4 sw=4 et:
  269. -->