Merge branch 'master' of git://github.com/yiisoft/yii2

This commit is contained in:
maxmirazh33
2014-08-24 22:56:29 +07:00
82 changed files with 354 additions and 155 deletions

View File

@ -151,6 +151,7 @@ Testing
-------
* [Overview](test-overview.md)
* [Testing environment setup](test-endvironment-setup.md)
* [Unit Tests](test-unit.md)
* [Functional Tests](test-functional.md)
* [Acceptance Tests](test-acceptance.md)

View File

@ -293,7 +293,7 @@ line:
migration history information. It defaults to `migration`. The table
structure is `version varchar(255) primary key, apply_time integer`.
* `connectionID`: string, specifies the ID of the database application component.
* `db`: string, specifies the ID of the database application component.
Defaults to 'db'.
* `templateFile`: string, specifies the path of the file to be served as the code
@ -337,3 +337,45 @@ the console application's configuration file like the following,
Now if we run the `migrate` command, the above configurations will take effect
without requiring us to enter the command line options every time. Other command options
can be also configured this way.
### Migrating with Multiple Databases
By default, migrations will be applied to the database specified by the `db` application component.
You may change it by specifying the `--db` option, for example,
```
yii migrate --db=db2
```
The above command will apply *all* migrations found in the default migration path to the `db2` database.
If your application works with multiple databases, some migrations should be applied to one database while
some others should be applied to another database. In this case, it is recommended that you create a base
migration class for each different database and override the [[yii\db\Migration::init()]] method like the following,
```php
public function init()
{
$this->db = 'db2';
parent::init();
}
```
If a migration needs to be applied to one database, you create a migration class by extending from the corresponding
base migration class.
Now if you run the `yii migrate` command, each migration will be applied to the corresponding database.
Because each migration has harcoded the DB connection, the `--db` option of the `migrate` command will have no effect.
If you still want to support changing DB connection via the `--db` option, you may take the following alternative
approach to work with multiple databases.
For each database, create a migration path and save all corresponding migration classes there. To apply migrations,
run the command as follows,
```
yii migrate --migrationPath=@app/migrations/db1 --db=db1
yii migrate --migrationPath=@app/migrations/db2 --db=db2
...
```

View File

@ -1,16 +1,18 @@
Versioning
==========
Your APIs should be versioned. Unlike Web applications which you have full control on both client side and server side
code, for APIs you usually do not have control of the client code that consumes the APIs. Therefore, backward
compatibility (BC) of the APIs should be maintained whenever possible, and if some BC-breaking changes must be
introduced to the APIs, you should bump up the version number. You may refer to [Semantic Versioning](http://semver.org/)
for more information about designing the version numbers of your APIs.
A good API is *versioned*: changes and new features are implemented in new versions of the API instead of continually altering just one version. Unlike Web applications, with which you have full control of both the client-side and server-side
code, APIs are meant to be used by clients beyond your control. For this reason, backward
compatibility (BC) of the APIs should be maintained whenever possible. If a change that may break BC is necessary, you should introduce it in new version of the API, and bump up the version number. Existing clients can continue to use the old, working version of the API; and new or upgraded clients can get the new functionality in the new API version.
Regarding how to implement API versioning, a common practice is to embed the version number in the API URLs.
For example, `http://example.com/v1/users` stands for `/users` API of version 1. Another method of API versioning
which gains momentum recently is to put version numbers in the HTTP request headers, typically through the `Accept` header,
like the following:
> Tip: Refer to [Semantic Versioning](http://semver.org/)
for more information on designing API version numbers.
One common way to implement API versioning is to embed the version number in the API URLs.
For example, `http://example.com/v1/users` stands for the `/users` endpoint of API version 1.
Another method of API versioning,
which has gained momentum recently, is to put the version number in the HTTP request headers. This is typically done through the `Accept` header:
```
// via a parameter
@ -19,16 +21,16 @@ Accept: application/json; version=v1
Accept: application/vnd.company.myapp-v1+json
```
Both methods have pros and cons, and there are a lot of debates about them. Below we describe a practical strategy
of API versioning that is kind of a mix of these two methods:
Both methods have their pros and cons, and there are a lot of debates about each approach. Below you'll see a practical strategy
for API versioning that is a mix of these two methods:
* Put each major version of API implementation in a separate module whose ID is the major version number (e.g. `v1`, `v2`).
Naturally, the API URLs will contain major version numbers.
* Within each major version (and thus within the corresponding module), use the `Accept` HTTP request header
to determine the minor version number and write conditional code to respond to the minor versions accordingly.
For each module serving a major version, it should include the resource classes and the controller classes
serving for that specific version. To better separate code responsibility, you may keep a common set of
For each module serving a major version, the module should include the resource and controller classes
serving that specific version. To better separate code responsibility, you may keep a common set of
base resource and controller classes, and subclass them in each individual version module. Within the subclasses,
implement the concrete code such as `Model::fields()`.
@ -86,11 +88,11 @@ return [
];
```
As a result, `http://example.com/v1/users` will return the list of users in version 1, while
As a result of the above code, `http://example.com/v1/users` will return the list of users in version 1, while
`http://example.com/v2/users` will return version 2 users.
Using modules, code for different major versions can be well isolated. And it is still possible
to reuse code across modules via common base classes and other shared classes.
Thanks to modules, the code for different major versions can be well isolated. But modules make it still possible
to reuse code across the modules via common base classes and other shared resources.
To deal with minor version numbers, you may take advantage of the content negotiation
feature provided by the [[yii\filters\ContentNegotiator|contentNegotiator]] behavior. The `contentNegotiator`
@ -101,7 +103,7 @@ For example, if a request is sent with the HTTP header `Accept: application/json
after content negotiation, [[yii\web\Response::acceptParams]] will contain the value `['version' => 'v1']`.
Based on the version information in `acceptParams`, you may write conditional code in places
such as actions, resource classes, serializers, etc.
such as actions, resource classes, serializers, etc. to provide the appropriate functionality.
Since minor versions require maintaining backward compatibility, hopefully there are not much
Since minor versions by definition require maintaining backward compatibility, hopefully there would not be many
version checks in your code. Otherwise, chances are that you may need to create a new major version.

View File

@ -0,0 +1,33 @@
Testing environment setup
======================
> Note: This section is under development.
Yii2 has officially maintained integration with [`Codeception`](https://github.com/Codeception/Codeception) testing
framework that allows you to create the following test types:
- [Unit testing](test-unit.md) - verifies that a single unit of code is working as expected;
- [Functional testing](test-functional.md) - verifies scenarios from a user's perspective via browser emulation;
- [Acceptance testing](test-acceptance.md) - verifies scenarios from a user's perspective in a browser.
Yii provides ready to use test sets for all three test types in both
[`yii2-basic`](https://github.com/yiisoft/yii2/tree/master/apps/basic) and
[`yii2-advanced`](https://github.com/yiisoft/yii2/tree/master/apps/advanced) application templates.
In order to run tests you need to install [Codeception](https://github.com/Codeception/Codeception). A good way to
install it is the following:
```
composer global require "codeception/codeception=2.0.*"
composer global require "codeception/specify=*"
composer global require "codeception/verify=*"
```
If you've never used Composer for global packages before, run `composer global status`. It should output:
```
Changed current directory to <directory>
```
Then add `<directory>/vendor/bin` to you `PATH` environment variable. Now we're able to use `codecept` from command
line globally.

View File

@ -305,13 +305,13 @@ yii fixture User UserProfile
yii fixture User --append
// load all fixtures
yii fixture/load *
yii fixture/load "*"
// same as above
yii fixture *
yii fixture "*"
// load all fixtures except ones
yii fixture * -DoNotLoadThisOne
yii fixture "*" -DoNotLoadThisOne
// load fixtures, but search them in different namespace. By default namespace is: tests\unit\fixtures.
yii fixture User --namespace='alias\my\custom\namespace'
@ -335,10 +335,10 @@ yii fixture/unload User
yii fixture/unload User,UserProfile
// unload all fixtures
yii fixture/unload all
yii fixture/unload "*"
// unload all fixtures except ones
yii fixture/unload all -DoNotUnloadThisOne
yii fixture/unload "*" -DoNotUnloadThisOne
```

View File

@ -1,44 +1,75 @@
Testing
=======
> Note: This section is under development.
TODO:
- https://github.com/yiisoft/yii2/blob/master/extensions/codeception/README.md
Testing is an important part of software development. Whether we are aware of it or not, we conduct testing continuously.
For example, when we write a class in PHP, we may debug it step by step or simply use echo or die statements to verify
that implementation is correct. In case of web application we're entering some test data in forms to ensure the page
interacts with us as expected. The testing process could be automated so that each time when we need to test something,
we just need to call up the code that perform testing for us. This is known as automated testing, which is the main topic
of testing chapters.
that implementation works according to our initial plan. In case of web application we're entering some test data in forms
to ensure the page interacts with us as expected. The testing process could be automated so that each time when we need
to verify something, we just need to call up the code that do it for us. The code that verifies that result matches what
we've planned is called test and the process of its creation and further execution is known as automated testing, which
is the main topic of testing chapters.
The testing support provided by Yii includes:
- [Unit testing](test-unit.md) - verifies that a single unit of code is working as expected.
- [Functional testing](test-functional.md) - verifies scenarios from a user's perspective via browser emulation.
- [Acceptance testing](test-acceptance.md) - verifies scenarios from a user's perspective in a browser.
Developing with tests
------------------
Yii provides ready to use test sets for all three testing types in both basic and advanced application templates.
Test-Driven Development (TDD) and Behavior-Driven Development (BDD) are approaches of developing
software by describing behavior of a piece of code or the whole feature as a set of scenarios or tests before
writing actual code and only then creating the implementation that allows these tests to pass verifying that intended
behavior is achieved.
Test environment setup
----------------------
The process of developing a feature is the following:
In order to run tests with Yii you need to install [Codeception](http://codeception.com/). A good way to install it is
the following:
- Create a new test that describes a feature to be implemented.
- Run new test and make sure it fails. It is expected since there's no implementation yet.
- Write simple code to make the new test pass.
- Run all tests and make sure they all pass.
- Improve code and make sure tests are still OK.
```
composer global require "codeception/codeception=2.0.*"
composer global require "codeception/specify=*"
composer global require "codeception/verify=*"
```
After it's done the process is repeated again for another feature or improvement. If existing feature is to be changed,
tests should be changed as well.
If you've never used Composer for global packages run `composer global status`. It should output:
> **Tip**: If you feel that you are loosing time doing a lot of small and simple iterations try covering more by your
> test scenario so you do more before executing tests again. If you're debugging too much try doing the opposite.
```
Changed current directory to <directory>
```
The reason to create tests before doing any implemenation is that it allows you to focus on what do we want to achieve
and fully dive into "how to do it" afterwards. Usually it leads to better abstractions and easier test maintenance when
it comes to feature adjustments in for of less coupled components.
Then add `<directory>/vendor/bin` to you `PATH` environment variable. Now we're able to use `codecept` from command
line globally.
So to sum up pros of such approach are the following:
- Keeps you focused on one thing at a time so both planning and implementation are getting better.
- Results in test-covering more features in greater detail i.e. if tests are OK most probably nothing's broken.
In the long term it usually gives you a good time-saving effect.
> **Tip**: If you want to know more about the principles for gathering software requirements and modeling the subject
> matter it's good to learn [Domain Driven Development (DDD)](https://en.wikipedia.org/wiki/Domain-driven_design).
When and how to test
------------------
While test first approach described above makes sense for long term and relatively complex projects it could be overkill
for simpler ones. There are some indicators of when it's appropriate:
- Project is already large and complex.
- Project requirements are starting to get complex. Project grows constantly.
- Project is meant to be long term.
- The cost of the failure is too high.
There's nothing wrong in creating tests covering behavior of existing implementation.
- Project is a legacy one to be gradually renewed.
- You've got a project to work on and it has no tests.
In some cases any form of automated testing could be overkill:
- Project is simple and isn't getting any complex.
- It's one-time project that's going to be expired.
Still if you have time it's good to automate testing in these cases as well.
Further reading
-------------
- Test Driven Development: By Example / Kent Beck. ISBN: 0321146530.

View File

@ -77,6 +77,9 @@ file via the `appconfig` option when executing the command:
yii <route> --appconfig=path/to/config.php ...
```
> **Note**: When using `*` in console don't forget to quote it as `"*"` in order to avoid executing it as a shell
> command.
Creating your own console commands
----------------------------------
@ -151,3 +154,8 @@ public function actionIndex()
return 0;
}
```
There are some predefined constants you can use:
- `Controller::EXIT_CODE_NORMAL` with value of `0`;
- `Controller::EXIT_CODE_ERROR` with value of `1`.