@@ -79,129 +79,6 @@ The most important part is the `_generated\AcceptanceTesterActions` trait, which |
79 | 79 | It knows which module executes which action and passes parameters into it. |
80 | 80 | This trait was created by running `codecept build` and is regenerated each time module or configuration changes. |
81 | 81 |
|
82 | | -<<<<<<< HEAD |
83 | | -
|
84 | | -## PageObjects |
85 | | -
|
86 | | -For acceptance and functional testing, we will not only need to have common actions being reused across different tests, |
87 | | -we should have buttons, links and form fields being reused as well. For those cases we need to implement |
88 | | -the [PageObject pattern](http://docs.seleniumhq.org/docs/06_test_design_considerations.jsp#page-object-design-pattern), |
89 | | -which is widely used by test automation engineers. The PageObject pattern represents a web page as a class |
90 | | -and the DOM elements on that page as its properties, and some basic interactions as its methods. |
91 | | -PageObjects are very important when you are developing a flexible architecture of your tests. |
92 | | -Do not hard-code complex CSS or XPath locators in your tests but rather move them into PageObject classes. |
93 | | -
|
94 | | -Codeception can generate a PageObject class for you with command: |
95 | | -
|
96 | | -{% highlight bash %} |
97 | | -
|
98 | | -php vendor/bin/codecept generate:pageobject Login |
99 | | -
|
100 | | -{% endhighlight %} |
101 | | -
|
102 | | -This will create a `Login` class in `tests/_support/Page`. |
103 | | -The basic PageObject is nothing more than an empty class with a few stubs. |
104 | | -It is expected that you will populate it with the UI locators of a page it represents |
105 | | -and then those locators will be used on a page. |
106 | | -Locators are represented with public static properties: |
107 | | -
|
108 | | -{% highlight php %} |
109 | | -
|
110 | | -<?php |
111 | | -namespace Page; |
112 | | -
|
113 | | -class Login |
114 | | -{ |
115 | | - public static $URL = '/login'; |
116 | | -
|
117 | | - public static $usernameField = '#mainForm #username'; |
118 | | - public static $passwordField = '#mainForm input[name=password]'; |
119 | | - public static $loginButton = '#mainForm input[type=submit]'; |
120 | | -} |
121 | | -
|
122 | | -{% endhighlight %} |
123 | | -
|
124 | | -And this is how this page object can be used in a test: |
125 | | -
|
126 | | -{% highlight php %} |
127 | | -
|
128 | | -<?php |
129 | | -$I->amOnPage(\Page\Login::$URL); |
130 | | -$I->fillField(\Page\Login::$usernameField, 'bill evans'); |
131 | | -$I->fillField(\Page\Login::$passwordField, 'debby'); |
132 | | -$I->click(\Page\Login::$loginButton); |
133 | | -$I->see('Welcome, bill'); |
134 | | - |
135 | | -{% endhighlight %} |
136 | | - |
137 | | -As you see, you can freely change markup of your login page, and all the tests interacting with this page |
138 | | -will have their locators updated according to properties of LoginPage class. |
139 | | - |
140 | | -But let's move further. The PageObject concept specifies that the methods for the page interaction should also be stored in a PageObject class. |
141 | | - |
142 | | -Let's define a `login` method in this class: |
143 | | - |
144 | | -{% highlight php %} |
145 | | - |
146 | | -<?php |
147 | | -namespace Page; |
148 | | -
|
149 | | -class Login |
150 | | -{ |
151 | | - public static $URL = '/login'; |
152 | | -
|
153 | | - public static $usernameField = '#mainForm #username'; |
154 | | - public static $passwordField = '#mainForm input[name=password]'; |
155 | | - public static $loginButton = '#mainForm input[type=submit]'; |
156 | | -
|
157 | | - /** |
158 | | - * @var AcceptanceTester |
159 | | - */ |
160 | | - protected $tester; |
161 | | -
|
162 | | - // we inject AcceptanceTester into our class |
163 | | - public function __construct(\AcceptanceTester $I) |
164 | | - { |
165 | | - $this->tester = $I; |
166 | | - } |
167 | | - |
168 | | - public function login($name, $password) |
169 | | - { |
170 | | - $I = $this->tester; |
171 | | - |
172 | | - $I->amOnPage(self::$URL); |
173 | | - $I->fillField(self::$usernameField, $name); |
174 | | - $I->fillField(self::$passwordField, $password); |
175 | | - $I->click(self::$loginButton); |
176 | | - } |
177 | | -} |
178 | | - |
179 | | -{% endhighlight %} |
180 | | - |
181 | | -If you specify which object you need for a test, Codeception will try to create it using the dependency injection container. |
182 | | -In the case of a PageObject you should declare a class as a parameter for a test method: |
183 | | - |
184 | | -{% highlight php %} |
185 | | - |
186 | | -<?php |
187 | | -class UserCest |
188 | | -{ |
189 | | - function showUserProfile(AcceptanceTester $I, \Page\Login $loginPage) |
190 | | - { |
191 | | - $loginPage->login('bill evans', 'debby'); |
192 | | - $I->amOnPage('/profile'); |
193 | | - $I->see('Bill Evans Profile', 'h1'); |
194 | | - } |
195 | | -} |
196 | | - |
197 | | -{% endhighlight %} |
198 | | - |
199 | | -The dependency injection container can construct any object that requires any known class type. |
200 | | -For instance, `Page\Login` required `AcceptanceTester`, and so it was injected into `Page\Login` constructor, |
201 | | -and PageObject was created and passed into method arguments. You should explicitly specify |
202 | | -the types of required objects for Codeception to know what objects should be created for a test. |
203 | | -Dependency Injection will be described in the next chapter. |
204 | | -======= |
205 | 82 | > Use actor classes to set common actions which can be used accross a suite. |
206 | 83 |
|
207 | 84 |
|
@@ -405,98 +282,6 @@ If you feel like adding too many actions into your Actor class |
405 | 282 |
|
406 | 283 | > Use StepObjects when you have multiple areas of applications or multiple roles. |
407 | 284 |
|
408 | | ->>>>>>> 82997221b762bc09876379f9f30906c9d364a4f5 |
409 | | - |
410 | | -## StepObjects |
411 | | - |
412 | | -StepObjects are great if you need some common functionality for a group of tests. |
413 | | -Let's say you are going to test an admin area of a site. You probably won't need the same actions from the admin area |
414 | | -while testing the front end, so it's a good idea to move these admin-specific tests into their own class. |
415 | | -We call such a classes StepObjects. |
416 | | - |
417 | | -Lets create an Admin StepObject with the generator: |
418 | | - |
419 | | -{% highlight bash %} |
420 | | - |
421 | | -php vendor/bin/codecept generate:stepobject acceptance Admin |
422 | | - |
423 | | -{% endhighlight %} |
424 | | - |
425 | | -You can supply optional action names. Enter one at a time, followed by a newline. |
426 | | -End with an empty line to continue to StepObject creation. |
427 | | - |
428 | | -{% highlight bash %} |
429 | | - |
430 | | -php vendor/bin/codecept generate:stepobject acceptance Admin |
431 | | -Add action to StepObject class (ENTER to exit): loginAsAdmin |
432 | | -Add action to StepObject class (ENTER to exit): |
433 | | -StepObject was created in /tests/acceptance/_support/Step/Acceptance/Admin.php |
434 | | - |
435 | | -{% endhighlight %} |
436 | | - |
437 | | -This will generate a class in `/tests/_support/Step/Acceptance/Admin.php` similar to this: |
438 | | - |
439 | | -{% highlight php %} |
440 | | - |
441 | | -<?php |
442 | | -namespace Step\Acceptance; |
443 | | -
|
444 | | -class Admin extends \AcceptanceTester |
445 | | -{ |
446 | | - public function loginAsAdmin() |
447 | | - { |
448 | | - $I = $this; |
449 | | - } |
450 | | -} |
451 | | -
|
452 | | -{% endhighlight %} |
453 | | -
|
454 | | -As you see, this class is very simple. It extends the `AcceptanceTester` class, |
455 | | -meaning it can access all the methods and properties of `AcceptanceTester`. |
456 | | -
|
457 | | -The `loginAsAdmin` method may be implemented like this: |
458 | | -
|
459 | | -{% highlight php %} |
460 | | -
|
461 | | -<?php |
462 | | -namespace Step\Acceptance; |
463 | | -
|
464 | | -class Admin extends \AcceptanceTester |
465 | | -{ |
466 | | - public function loginAsAdmin() |
467 | | - { |
468 | | - $I = $this; |
469 | | - $I->amOnPage('/admin'); |
470 | | - $I->fillField('username', 'admin'); |
471 | | - $I->fillField('password', '123456'); |
472 | | - $I->click('Login'); |
473 | | - } |
474 | | -} |
475 | | - |
476 | | -{% endhighlight %} |
477 | | - |
478 | | - |
479 | | -StepObject can be instantiated automatically when used inside the Cest format: |
480 | | - |
481 | | -{% highlight php %} |
482 | | - |
483 | | -<?php |
484 | | -class UserCest |
485 | | -{ |
486 | | - function showUserProfile(\Step\Acceptance\Admin $I) |
487 | | - { |
488 | | - $I->loginAsAdmin(); |
489 | | - $I->amOnPage('/admin/profile'); |
490 | | - $I->see('Admin Profile', 'h1'); |
491 | | - } |
492 | | -} |
493 | | - |
494 | | -{% endhighlight %} |
495 | | - |
496 | | -If you have a complex interaction scenario, you may use several step objects in one test. |
497 | | -If you feel like adding too many actions into your Actor class |
498 | | -(which is AcceptanceTester in this case) consider moving some of them into separate StepObjects. |
499 | | - |
500 | 285 |
|
501 | 286 | ## Conclusion |
502 | 287 |
|
|