You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
<divclass="alert alert-error">This chapter is <strong>deprecated</strong>. Don't read it and forget everything I might have read.
9
-
Use classical <ahref="http://codeception.com/docs/06-UnitTests">Unit Tests</a> with some Codeception powers. <ahref="http://codeception.com/03-18-2013/scenario-unit-deprecated.html">Here is why it it happened</a></div>
8
+
In this chapter we will cover some technics and options that you can use to improve your testing experience and stay with better organization of your project.
10
9
10
+
## Interactive Console
11
11
12
-
Each function is like a little a little application in itself. It's the simplest and least divisible part of the program. It's natural to test it the way we test the application as a whole. Codeception unit tests are very similar to acceptance tests, with some additional features that simplify code testing.
12
+
Interactive console was added to try Codeception commands before executing them inside a test.
13
+
This feature was introduced in 1.6.0 version.
13
14
14
-
Unit tests are required to be as readable as possible. They should be clean and easy to understand. Codeception helps the developer to follow these practices.
Now you can execute all commands of appropriate Guy class and see immidiate results. That is especially useful for when used with Selenium modules. It always takes too long to launch Selenium and browser for tests. But with console you can try different selectors, and different commands, and then write a test that would pass for sure when executed.
32
25
33
-
{% endhighlight %}
26
+
And a special hint: show your boss how you can nicely manipulate web pages with console and Selenium. With this you can convince him that it is easy to automate this steps and introduce acceptance testing to the project.
34
27
35
-
The similar test in PHPUnit would look like this:
28
+
## Running from different folders.
29
+
30
+
If you have several project with Codeception tests in it you can use one `codecept.phar` file to run their tests.
31
+
You can pass a `-c` option to any Codeception command, excluding `bootstrap` to execute codeception in other directory.
To create a project in directory other then current just provide it's path as a parameter.
41
+
42
+
{% highlight php %}
43
+
codecept.phar bootstrap ~/projects/drupal/
44
44
45
45
{% endhighlight %}
46
46
47
-
Well, PHPUnit wins here: its test is shorter and readable. **There is no practical reason for using Codeception for testing simple methods**. But not all functions can be executed and tested that way. Whenever a function has dependencies and it's results can't be so easily observable, Codeception will be quite useful.
47
+
Basically `-c` option allows you specify not only the path but a config file to be used. Thus, you can have several `codeception.yml` file for your test suite. You may use it to specify different environments and settings. Just pass a filename into `-c` parameter to execute tests with specific config settings.
48
+
49
+
## Groups
50
+
51
+
There are several ways to execute bunch of tests. You can run tests from specific directory:
52
+
53
+
{% highlight php %}
54
+
codecept.phar run tests/acceptance/admin
48
55
49
-
Using Codeception for unit testing is like using a framework for web development. Even if it's hard to create a 'hello world' page with Symfony, Zend, or Yii, they help you build complex web applications.
56
+
{% endhighlight %}
50
57
51
-
## Testing the Controller
58
+
Or execute one (or several) specific groups of tests:
52
59
53
-
It's natural to assume that you are using PHP for web development.
54
-
Generally, web applications use the MVC (Model-View-Controller) pattern.
55
-
Let's show how Codeception simplifies unit testing for controller classes.
60
+
{% highlight php %}
61
+
codecept.phar run -g admin -g editor
62
+
63
+
{% endhighlight %}
56
64
57
-
We have a controller class of an imaginary MVC framework:
65
+
In this case all tests that belongs to groups admin or editor will be executed. Groups concept were taken from PHPUnit and in classical PHPUnit tests they behave just in the same way. To add Cept to the group - use `$scenario` variable:
58
66
59
67
{% highlight php %}
60
68
61
69
<?php
62
-
class UserController extends AbstractController {
63
-
64
-
public function show($id)
65
-
{
66
-
$user = $this->db->find('users',$id);
67
-
if (!$user) return $this->render404('User not found');
For Tests and Cests you can use annotation `@group` to add a test to the group.
75
83
76
-
We want to test this `show` method. As you can see it's rather different then the `validateEmail` function from the previous example.
77
-
This is because the `show` method relies on other functions and classes.
84
+
{% highlight php %}
78
85
79
-
When we are performing unit testing we should ignore all those dependencies in the test. We don't worry about how the render method works, or how the `$this->db` searches for a user instance.
80
-
The only thing we test is the behavior of the `show` action.
81
-
To test such a method, we should replace classes with their [stubs, and use mocks](http://martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs) for assertions.
For unit tests Codeception provides a different test file format. It's called Cest (Test + Cept = Cest).
101
+
In case you want to get a class-like structure for your Cepts, instead of plain PHP, you can use Cest format.
102
+
It is very simple and is fully compatible with Cept scenarios. It means If you feel like your test is long enough and you want to split it - you can easily move it into class.
86
103
87
-
Here is the Codeception test for the 'show' action:
->seeMethodInvoked($controller, 'render404','User not found')
115
-
->seeMethodNotInvoked($controller, 'render');
127
+
// tests
128
+
public function tryToTest(\WebGuy $I) {
129
+
116
130
}
117
131
}
118
132
?>
119
133
120
134
{% endhighlight %}
121
135
122
-
This test is written as a simple scenario. Every command of the scenario clearly describes the action being taken. Let's review this code.
123
-
124
-
First of all, take a look at the `Cest` suffix. By it, Codeception knows it's a Cest testing class. The public property `$class` is just as important. It defines the class which is being tested. Each public method of `$class` will be treated as a test. Please note, that the name of each test method is the same as the method which is actually being tested. In other words, to test `UserController->show` we use `UserControllerCest->show`, `UserController->edit => UserControllerCest->edit`, etc. The only parameter of the test method is the CodeGuy class instance.
125
-
126
-
With the CodeGuy we write a scenario for unit testing. The action `haveFakeClass` declares that we will use a stub in our testing. By using this command Codeception will dynamically create a mock for this class.
127
-
Codeception uses a wrapper over PHPUnit's mocking library. It can create various stubs in a simple way. Later we will review this tool more deeply.
136
+
**Each public method of Cest (except, those starting from `_`) will be executed as a test** and will receive Guy class as the first parameter and `$scenario` variable as a second.
128
137
129
-
For `UserController` we redefine all of it's methods, except the tested one, with dummies.
130
-
For the `$db` property, which is supposed to be a DbConnector (Database class) instance we redefine its `find` method. Depending on a parameter it is supposed to return a User model or `null`.
138
+
In `_before` and `_after` method you can use common setups, teardowns for the tests in the class. That actually makes Cest tests more flexible, then Cepts that rely only on similar methdods in Helper classes.
131
139
132
-
Next we connect both stubbed classes. We'd normally use:
140
+
As you see we are passing Guy class into `tryToTest` stub. That allows us to write a scenarios the way we did before.
133
141
134
142
{% highlight php %}
135
143
136
144
<?php
137
-
$controller->db = $db;
145
+
class BasicCest
146
+
{
147
+
// test
148
+
public function checkLogin(\WebGuy $I) {
149
+
$I->wantTo('log in to site');
150
+
$I->amOnPage('/');
151
+
$I->click('Login');
152
+
$I->fillField('username', 'jon');
153
+
$I->fillField('password','coltrane');
154
+
$I->click('Enter');
155
+
$I->see('Hello, Jon');
156
+
$I->seeInCurrentUrl('/account');
157
+
}
158
+
}
138
159
?>
139
160
140
161
{% endhighlight %}
141
162
142
-
But the `$controller->db` property might be protected. By using the`setProperty` command we can even set the values of protected and private properties! This can be done with the power of [Reflection](http://php.net/manual/en/book.reflection.php).
163
+
But there is a limiation in Cest files. It can't work with `_bootstrap.php` the way we did in scenario tests.
164
+
It was useful to store some variables in bootstraps that should be passed into scenario.
165
+
In Cest files you should inject all external variables manually, using static or global variables.
143
166
144
-
The environment has been prepared. There are no dependencies left. Now we can concentrate on the actual testing.
167
+
As a workaround you can choose [Fixtures](https://github.com/Codeception/Codeception/blob/master/src/Codeception/Util/Fixtures.php) class which is nothing more then global storage to your variables. You can pass variables from `_bootstrap.php` or any other place just with `Fixtures::add()` call. But probably you can use Cest classes `_before` and `_after` methods to load fixtures on the start of test, and deleting them afterwards. Pretty useful too.
145
168
146
-
First test case -- we execute the `show` action for an existing user.
147
-
We see that the tested method returns true and the method `render` was invoked.
169
+
As you see, Cest class have no parent like `\Codeception\TestCase\Test` or `PHPUnit_Framework_TestCase`. That was done intentionally. This allows you to extend class any time you wnat by attaching any meta-testing class to it's parent. In meta class you can write common behaviors and workarounds that may be used in child class. But don't forget to make them `protected` so they won't be executed as a tests themselves.
148
170
149
-
To ensure 100% code coverage of this method we should test the negative scenario too, avoiding the [Happy Path anti-pattern](http://www.ibm.com/developerworks/opensource/library/os-junit/).
171
+
Also you can define `_failed` method in Cest class which will be called if test finished with `error` or fail.
150
172
151
-
Our second test case is running the `show` action for a non-existent user. The 404 page should be rendered in this case.
173
+
## Refactoring
152
174
153
-
The `expect` command we use here is as good as a comment. We describe the expected result if it's not obvious. Actually we can guess that the `UserController->show` method would show a user. But we can't be sure what would happen if the user doesn't exist.
154
-
That's why we use 'expect' to describe the function description.
175
+
As test base growth they will require refactoring, sharing common variables and behaviors. The classical example for this is `login` action which will be called for maybe every test of your test suite. It's wise to make it written one time and use it in all tests.
155
176
156
-
### PHPUnit Example
157
-
158
-
To prove Codeception was useful for testing the controller, we will write the same test in PHPUnit.
159
-
Remember, it can be run with Codeception too.
177
+
It's pretty obvious that for such cases you can use your own PHP classes to define such methods.
160
178
161
179
{% highlight php %}
162
180
163
-
<?php
164
-
165
-
class UserControllerTest extends PHPUnit_Framework_TestCase
This test is 1.5 times longer. One test is split into two. Mocking requires strong knowledge of the PHPUnit API. It's hard to understand the behavior of the tested method `show` without looking into its code.
201
-
Nevertheless this test is quite readable.
202
197
203
-
Let's analyze why we split the one test into two.
204
-
We are testing one feature, but two different behaviors. This can be argued, but we suppose that showing a "404 page" can't be treated as a feature of your product.
198
+
This file can be required in `_bootstrap.php` file
205
199
206
-
Having two tests instead of one is a technical, not a logical, limitation of PHPUnit.
207
-
That's because you can't test the method `render` invoked once and not invoked in a single test. All mocked methods invocations are checked at the very end of the test. Thus, having expectations for both 'render once' and 'render never', we will only see that the last one has been performed.
You are free to decide the testing framework you will use for unit tests. Codeception and PHPUnit run the same engine.
212
-
Codeception provides you with a cool DSL to simplify your unit tests. You are writing the scenario definitions, not the actual executed code. Behind the scenes all the dirty work is done by Codeception. You write only the testing logic.
213
+
<?php
214
+
$I = new WebGuy($scenario);
215
+
TestCommons::logMeIn($I);
216
+
?>
217
+
218
+
{% endhighlight %}
219
+
220
+
You should get the idea by now. Codeception doesn't provide any particular strategy for you to manage the tests. But it is flexible enough to create all the support classes you need for your test suites. The same way, with custom classes you can implement `PageObject` and `StepObject` patterns.
221
+
222
+
## PageObject and StepObjects
223
+
224
+
In next versions Codeception will provide PageObjects and StepObjects out of the box.
225
+
But today we encourage you to try your own implementations. Whe have some nice blogpost for you to learn what are PageObjects, why you ever want to use them and how they can be implemented.
226
+
227
+
*[Ruling the Swarm (of Tests)](http://phpmaster.com/ruling-the-swarm-of-tests-with-codeception/) by Michael Bodnarchuk.
228
+
*[Implementing Page Objects in Codeception](http://jonstuff.blogspot.ca/2013/05/implementing-page-objects-in.html) by Jon Phipps.
229
+
230
+
## Conclusion
213
231
232
+
Codeception is a framework which may look simple at first sight. But it allows you to build powerful test with one APIs, refactor them, and write them faster using interactive console. Codeception tests can easily be organized with groups or cest classes. Probably too much abilities for the one framework. But nevertheless Codeception follows the KISS pricinple: it's easy to start, easy to learn, easy to extend.
0 commit comments