Codeception Tests with DDEV and Selenium
Tags: codeception, ddev, selenium, docker
Published: 2020-03-28 12:49:59

Running behaviour driven tests like [Codeception](https://codeception.com/) can bring some advantages to your project, and will help you detect if you break functionality on updates, changes etc. 

This blog post will help you setup the basics needed for developing your own tests and running them locally with [ddev](https://www.ddev.com/). 

## Prerequisits
* DDEV installed on you local machine 
* Project setup locally with [ddev](https://www.ddev.com/), in this case I will use a plain `phpinfo()` site to lower complexity. 

If you don't have a setup locally already, you can do this by follwoing the [ddev get started guide](https://www.ddev.com/get-started/), you can also close my repository on github, then you will not need to do the changes your self to check the prove of concept. 

```
$ git clone https://github.com/tomasnorre/ddev-codeception-poc.git
$ cd ddev-codeception-poc
$ composer install
$ ddev start
```

If you choose the later one, you can skip the instructions in step 1-3, and go directly to executing the tests, but I would recommend you to do the steps manually.


## 1. Setup Selenium in DDEV

As you might already know ddev is based on docker, so to setup selenium, we need to add a custom `docker-composer.selenium.yml` to your ddev setup. 

```
$ cd /path/to/your/local/project/.ddev
$ touch docker-compose.selenium.yaml
```

Use you default editor to edit the `docker-compose.selenium.yaml` file. This configuration is take from the [Selenium Github Page](https://github.com/SeleniumHQ/docker-selenium#version-3), with a few adjustments, like the `version: '3.6'`, `container_name` and removing the `firefox` and `opera` part. 

```
version: '3.6'
services:
  selenium-hub:
    image: selenium/hub:3.141.59-20200326
    container_name: ddev-${DDEV_SITENAME}-selenium-hub
    ports:
      - "4444:4444"

  chrome:
    image: selenium/node-chrome:3.141.59-20200326
    container_name: ddev-${DDEV_SITENAME}-chrome
    volumes:
      - /dev/shm:/dev/shm
    depends_on:
      - selenium-hub
    environment:
      - HUB_HOST=selenium-hub
      - HUB_PORT=4444

```

Now you can restart you ddev project with `ddev restart` and you will have a selenium container running together with your project.

## 2. Install Codeception

Steps copied from [Codeception QuickStart Guide](https://codeception.com/quickstart)

```
$ composer require "codeception/codeception" --dev
$ php vendor/bin/codecept init acceptance

Let's prepare Codeception for acceptance testing
Create your tests and run them in real browser

? Where tests will be stored? (tests)
? Select a browser for testing (chrome)
  [0] chrome
  [1] firefox
 > 0
 Ensure that you have Selenium Server and ChromeDriver installed before running tests
? Start url for tests (http://localhost)
 Created test directories inside at tests
 Adding codeception/module-webdriver for WebDriver to composer.json
1 new packages added to require-dev
? composer.json updated. Do you want to run "composer update"? (y/n) y
```

After this we know have a `codeception.yml` in our root folder, next to `composer.json` and we have a `tests` folder, also in our root folder, that is created and populated with Codeception setup. 

As I didn't adjust the start url as part of the initiation I need to adjust it manually. 
Open the `codeception.yml` and adjust 

```
enabled:
    - WebDriver:
        url: http://localhost/
```
to

```
enabled:
    - WebDriver:
        url: http://web/
```

this will make the Codeception make requests against the docker-container named web, which is your application container, in a default ddev setup. 

## 3. Small Test example

In the `tests` folder we also have a file `LoginCest.php` which we are going to adjust, to add new tests. 

The naming of the file, isn't important at this stage, but I would suggest to have files and structures match your application and what you're testing, this keeps it easier to find, correct, and add new tests.

Open the `tests/LoginCest.php` with your editor. Currently it looks like this

```
<?php
class LoginCest 
{    
    public function _before(AcceptanceTester $I)
    {
        $I->amOnPage('/');
    }

    public function loginSuccessfully(AcceptanceTester $I)
    {
        // write a positive login test 
    }
    
    public function loginWithInvalidPassword(AcceptanceTester $I)
    {
        // write a negative login test
    }       
}
``` 

The `_before()` is actions that will be executed before every test. The two other tests are empty right now so if you run `php vendor/bin/codecept run` you will have a green test result.

Let us add some more meaning full tests. First remove the two empty tests and add

```
public function canSeePHPVersion(AcceptanceTester $I)
{
    $I->canSee('PHP Version 7.4.3');
}
```

This will check if it finds the string "PHP Version 7.4.3" on the page `/` as that's the page defined in the `_before()` function. 

Lets run the tests: 

```
$ php vendor/bin/codecept run

Codeception PHP Testing Framework v4.1.4
Powered by PHPUnit 8.5.2 by Sebastian Bergmann and contributors.
Running with seed:


Acceptance Tests (1) -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
✔ LoginCest: Can see php version (1.78s)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Time: 2.91 seconds, Memory: 12.00 MB

OK (1 test, 1 assertion)
```

This page used can also be changed by doing
```
public function canSeePHPVersion(AcceptanceTester $I)
{
    $I->amOnPage('/index.php');
    $I->canSee('PHP Version 7.4.3');
}
```

If you have failing tests, there will be a link echoed in your terminal

```
There was 1 failure:

---------
1) LoginCest: Can see php version
 Test  tests/LoginCest.php:canSeePHPVersion
 Step  Can see "PHP Version 7.4.2"
 Fail  Failed asserting that  on page /index.php
--> PHP Version 7.4.3
System Linux ddev-codeception-poc-web 4.19.76-linuxkit #1 SMP Thu Oct 17 19:31:58 UTC 2019 x86_64
Build Date Feb 24 2020 18:39:14
Server API FPM/FastCGI
Virtual Directory Support disabled
Configuration File (php.ini) Path /etc/php/7.4/fpm
Loaded Configuration File /etc/php/7.4/fp
[Content too long to display. See complete response in '/path/to/Projects/ddev-codeception-poc/tests/_output/' directory]
--> contains "PHP Version 7.4.2".

Scenario Steps:

 3. $I->canSee("PHP Version 7.4.2") at tests/LoginCest.php:12
 2. $I->amOnPage("/index.php") at tests/LoginCest.php:11
 1. $I->amOnPage("/") at tests/LoginCest.php:6

Artifacts:

Png: /path/to/Projects/ddev-codeception-poc/tests/_output/LoginCest.canSeePHPVersion.fail.png
Html: /path/to/Projects/ddev-codeception-poc/tests/_output/LoginCest.canSeePHPVersion.fail.html

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
```

The `png` version is a screenshot, so if the text/element you are looking for are below the fold, you will not see it. 

The `html` version is a scrollable version of the page, be aware that this might look quite rubbish, if the Javascript, css etc, isn't loaded correct, which often is the case, so might be difficult to spot the problem. 

## Conclusion

This is as you see a very small and compact example on how you can run Codeception in DDEV setup. 

I'm far from expert on the field, neither ddev nor Codeception, but if you have any questions feel free to reach out to me on [https://twitter.com/tomasnorre](https://twitter.com/tomasnorre).

I have also pushed the example setup to my github account, so if you want to discuss something, that could benefit others, you can create an issue at [https://github.com/tomasnorre/ddev-codeception-poc/issues](https://github.com/tomasnorre/ddev-codeception-poc/issues)