|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Using Codeception for Symfony projects" |
| 4 | +date: 2015-09-04 01:03:50 |
| 5 | +--- |
| 6 | + |
| 7 | +Codeception Testing Framework from its roots was a plugin of symfony 1 framework. Today Codeception is powered by Symfony components and can be used to run functional tests for practically any popular PHP framework. |
| 8 | + |
| 9 | +Why would you someone ever choose Codeception if Symfony is already have mature testing infrastructure. Well, here are our reasons. Codeception tests are: |
| 10 | + |
| 11 | +* **fast**, as each functional/integration test is wrapped into transaction using Doctrine ORM |
| 12 | +* **scenario-driven**, it means that tests are linear, described in easy to get PHP DSL |
| 13 | +* can be used for **testing complex interactions** inside functional tests. |
| 14 | +* **easy to write**, as Codeception already provides bundled actions and assertions for most popular use cases. |
| 15 | +* combine **all testing levels** (acceptance, functional, unit) in one tool. |
| 16 | + |
| 17 | + |
| 18 | +Today we will write how Codeception can be installed into a Symfony project and fit with its structure: we will put functional and unit tests to corresponding bundles, write acceptance tests for complete application, and use one runner to execute them all at once. |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +We will use official [symfony-demo](http://symfony.com/blog/introducing-the-symfony-demo-application) application in this example. Once you get it [installed](https://github.com/symfony/symfony-demo#installation) you should add Codeception testing framework to dev dependencies list in `composer.json`: |
| 23 | + |
| 24 | +{% highlight bash %} |
| 25 | +composer require --dev "codeception/codeception:~2.1" |
| 26 | +{% endhighlight %} |
| 27 | + |
| 28 | +As you may know Codeception has `bootstrap` command which create common file structure for acceptance, functional, and unit tests. However, as we decided we will follow Symfony way and skip creating global functional and unit tests. So we start with bootstrapping empty project without predefined suites. |
| 29 | + |
| 30 | +{% highlight bash %} |
| 31 | +php bin/codecept bootstrap --empty |
| 32 | +{% endhighlight %} |
| 33 | + |
| 34 | +As you may see, we got new `tests` directory and `codeception.yml` file created. Let's have acceptance tests there: |
| 35 | + |
| 36 | +{% highlight bash %} |
| 37 | +php bin/codecept g:suite acceptance |
| 38 | +{% endhighlight %} |
| 39 | + |
| 40 | +Acceptance tests are expected to test the site from an end-user's perspective. No matter how many unit tests you have in your projects you can't get without acceptance testing. What if you see a blank page even all unit tests passed. How could this happen? Maybe you rendered wrong template, maybe some scripts or styles were not loaded. Those things can't be handled with internal: unit or functional testing, however with acceptance tests you may be confident that UI is available for customers. |
| 41 | + |
| 42 | +That's why we recommend to have tests with real browser interaction. You can [learn more about acceptance testing](http://codeception.com/docs/03-AcceptanceTests) from our guides. |
| 43 | + |
| 44 | +But what about unit and functional tests? As we decided we will put them into bundles. *Symfony-demo* has only *AppBundle* included, so we will create new Codeception setup in `src/AppBundle`. Take a note that we want those tests to be placed in their own namespace: |
| 45 | + |
| 46 | +{% highlight bash %} |
| 47 | +php bin/codecept bootstrap --empty -c src/AppBundle --namespace AppBundle |
| 48 | +{% endhighlight %} |
| 49 | + |
| 50 | +We will also create `unit` and `functional` suites there: |
| 51 | + |
| 52 | +{% highlight bash %} |
| 53 | +php bin/codecept g:suite functional -c src/AppBundle |
| 54 | +php bin/codecept g:suite unit -c src/AppBundle |
| 55 | +{% endhighlight %} |
| 56 | + |
| 57 | +As you noticed we specify path to different Codeception setup with `-c` or `--config` option. |
| 58 | + |
| 59 | +Unit tests of Codeception are not quite different from regular PHPUnit tests. You can even copy your old PHPUnit tests to `src/AppBundle/tests/unit` and have Codeception run them. It is much more interesting to use Codeception to have functional tests replacing ones extending `Symfony\Bundle\FrameworkBundle\Test\WebTestCase` class. |
| 60 | + |
| 61 | +Let's have a test that will check that there is specific number of posts on a page. Symfony-demo app has the [similar test](https://github.com/symfony/symfony-demo/blob/master/src%2FAppBundle%2FTests%2FController%2FBlogControllerTest.php#L29) included: |
| 62 | + |
| 63 | +{% highlight php %} |
| 64 | +use AppBundle\Entity\Post; |
| 65 | +class BlogControllerTest extends WebTestCase |
| 66 | +{ |
| 67 | + public function testIndex() |
| 68 | + { |
| 69 | + $client = static::createClient(); |
| 70 | + $crawler = $client->request('GET', '/en/blog/'); |
| 71 | + $this->assertCount( |
| 72 | + Post::NUM_ITEMS, |
| 73 | + $crawler->filter('article.post'), |
| 74 | + 'The homepage displays the right number of posts.' |
| 75 | + ); |
| 76 | + } |
| 77 | +} |
| 78 | +{% endhighlight %} |
| 79 | + |
| 80 | + |
| 81 | +We will rewrite it in Codeception manner. At first we are generating new empty test case for it. We use scenario-driven test format called Cest: |
| 82 | + |
| 83 | +{% highlight bash %} |
| 84 | +php bin/codecept g:cest functional BlogCest -c src/AppBundle |
| 85 | +{% endhighlight %} |
| 86 | + |
| 87 | +And here goes the test: |
| 88 | + |
| 89 | +{% highlight php %} |
| 90 | +use AppBundle\Entity\Post; |
| 91 | +class BlogCest |
| 92 | +{ |
| 93 | + public function postsOnIndexPage(FunctionalTester $I) |
| 94 | + { |
| 95 | + $I->amOnPage('/en/blog/'); |
| 96 | + $I->seeNumberOfElements('article.post', Post::NUM_ITEMS); |
| 97 | + } |
| 98 | +} |
| 99 | +?> |
| 100 | +{% endhighlight %} |
| 101 | + |
| 102 | +As you see, Codeception test is shorter. It is simple, clean, and can be easily extended for more complex interactions. However, we are not ready to run it yet. We need to prepare Codeception to run functional tests inside Symfony context. For this we need to edit `src/AppBundle/tests/functional.yml` configuration file to enable modules: `Symfony2` and `Doctrine2` to use: |
| 103 | + |
| 104 | +{% highlight yaml %} |
| 105 | +class_name: FunctionalTester |
| 106 | +modules: |
| 107 | + enabled: |
| 108 | + - Symfony2: |
| 109 | + app_path: '../../app' |
| 110 | + var_path: '../../app' |
| 111 | + - Doctrine2: |
| 112 | + depends: Symfony2 |
| 113 | + - \AcmeBundle\Helper\Functional |
| 114 | +{% endhighlight %} |
| 115 | + |
| 116 | +The most important thing here is to provide valid app and var paths for Symfony. Also we are specifying that Doctrine's EntityManager should be taken from Symfony DIC. Let's run functional tests of AppBundle: |
| 117 | + |
| 118 | +{% highlight bash %} |
| 119 | +php bin/codecept run functional -c src/AppBundle |
| 120 | +{% endhighlight %} |
| 121 | + |
| 122 | +In this case you will see following output: |
| 123 | + |
| 124 | +{% highlight bash %} |
| 125 | +Codeception PHP Testing Framework v2.1.2 |
| 126 | +Powered by PHPUnit 4.8.2 by Sebastian Bergmann and contributors. |
| 127 | + |
| 128 | +AcmeBundle.functional Tests (1) ------------------------ |
| 129 | +Posts on index page (BlogCest::postsOnIndexPage) Ok |
| 130 | +-------------------------------------------------------- |
| 131 | + |
| 132 | + |
| 133 | +Time: 274 ms, Memory: 36.00Mb |
| 134 | + |
| 135 | +OK (1 test, 1 assertion) |
| 136 | +{% endhighlight %} |
| 137 | + |
| 138 | +But you can do much more with functional testing. You can insert/assert data with Doctrine by using prepared methods like `haveInRepository`, `seeInRepository` of [Doctrine2](http://codeception.com/docs/modules/Doctrine2) module. You can perform complex web interactions like filling forms, clicking links, following redirects and much more with methods of [Symfony2](http://codeception.com/docs/modules/Symfony2) module. Those modules are combined together and their methods are available in `FunctionalTester` class you are supposed to use for writing functional tests. If you are interested to see more complex Codeception tests, we've got [them for you](https://github.com/Codeception/symfony-demo/blob/2.1/src%2FAppBundle%2Ftest%2Ffunctional%2FPostCrudCest.php). |
| 139 | + |
| 140 | +Btw, you can use Symfony2 and Doctrine2 module for writing your unit and integration tests as well. |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +But how can we run acceptance tests of a project with tests from AppBundle together? We need to edit `codeception.yml` configuration file in project root to make it. Let's add those lines there: |
| 145 | + |
| 146 | +{% highlight yaml %} |
| 147 | +include: |
| 148 | + - src/*Bundle |
| 149 | +{% endhighlight %} |
| 150 | + |
| 151 | +That's it. For now Codeception will include all installations stored in Bundles on run. If you execute: |
| 152 | + |
| 153 | +{% highlight bash %} |
| 154 | +php bin/codecept run |
| 155 | +{% endhighlight %} |
| 156 | + |
| 157 | +you will probably see that `BlogCest` of `AppBundle` was executed as it was expected to. |
| 158 | + |
| 159 | + |
| 160 | +--- |
| 161 | + |
| 162 | + |
| 163 | + |
| 164 | +The most complex thing in starting using Codeception with Symfony is have it configured. Despite Codeception is auto-connecting to Symfony framework and Doctrine you still have to do some changes to follow Symfony structure. Please take a detailed look into [forked version of symfony-demo project](https://github.com/Codeception/symfony-demo) |
| 165 | +which we configured in the manner we described in this post. Please use similar configuration for all your Symfony projects. |
| 166 | + |
| 167 | +Start using Codeception and discover how complex things can be tested in really simple manner. And once again, even functional and integration tests are really fast, as we start transaction before each test and rollback it afterwards. Write them as many as you need to, do not rely on unit tests only! |
| 168 | + |
| 169 | +**P.S.** Symfony2 and Doctrin2 module is seeking for an active maintainer. If you work with Symfony and Codeception please [contact us](http://codeception.com/credits) to join Codeception team! |
0 commit comments