Get to Know: Codeception

13 August 2018 by Jenn Granger

It’s a brand new week and we know your tech brains are a-buzzing! This week we have a special guest blog from UKFast techie George who explains Codeception, what it means for PHP developers across the world and tips to make your coding life easier!

Codeception: What is it?

Codeception is a PHP testing framework used by developers to check the sites they work on. It splits tests up into individual suites: unit tests, functional tests and acceptance tests. It has been around for a while now (with the first commit appearing around seven years ago) and has a well-established and active community.

Laravel also has a great set of tools for testing apps – PHPUnit with Dusk pretty much gives you everything you’d need to write a full range of tests. However, Codeception working alongside Laravel provides some really handy extras that make writing tests even simpler.

Testing Suites

Codeception provides out-of-the-box solutions for three suites, each with different purposes and designed to be run at different intervals.

The Codeception unit test suite is the most basic one and runs on PHPUnit. It has full backwards compatibility with existing PHPUnit tests, making it simple to transfer existing test suites over to Codeception as they all have access to the same helpers, methods and traits. Codeception can either run PHPUnit test cases or you can use a custom Codeception one. There are very few differences between the two except syntax and access to Codeception’s module system. Unit tests are designed to be run on each save and to test individual methods and classes.

Functional tests are broader in scope than unit tests; they test your app within the scope of the framework by giving it an input (a Request object) and performing assertions on an output (the Response object). Typically, they are less stable than other tests as your app and the tests are run in the same environment continuously, so if your app isn’t designed to be run this way (e.g. using die, or exit statements) then the tests will often crash. This issue is mitigated by using Laravel, as it’s designed to be run continuously, and various community-supported drivers allow Codeception to mess with the internals of Laravel, so it’s able to run these tests. They are designed to be run fairly frequently, but not on each save, as they do take a bit of time to execute, and it’s unlikely that you’ll need to test the whole app this frequently.

The final suite is acceptance tests. These are the largest in scope and perform full, end-to-end tests, often emulated in a browser. They’re a great way to test your end-user requirements and give very realistic results. It’s also completely codebase agnostic, so it doesn’t matter if the site you’re testing is using WordPress, Java or something else, as the tests run as if from an external user. Due to the nature and speed of acceptance tests, they are only meant to be run occasionally – roughly on each commit. To execute acceptance tests, you need to use one of two drivers: PHP Browser or WebDriver.

What happens on each one?

PHP Browser:

Instead of running in an actual browser, it uses a combination of Guzzle and Symfony’s BrowserKit

It can’t test JavaScript, so if there’s any jQuery or Vue.js that make changes to the page then it won’t be able to make assertions on those changes

It just checks the HTML source, similar to functional tests

Only requires PHP with Curl, so it’s very easy to install and set up

It also executes relatively quickly

WebDriver on the other hand is a lot more feature complete, but heavier:

It uses Chrome or Firefox to execute the tests

Because of this, it can test content generated or manipulated by JavaScript

When making assertions, it actually checks to see if the text is visible on the screen instead of just checking the source, so if an element is obscuring the view of another, it will be able to detect this.

It can’t read response headers

It’s much harder to set up, as it requires either Selenium Standalone Server, Gecko Driver, ChromeDriver, or PhantomJS

It also executes much more slowly than PHPBrowser

The Codeception team recommend using WebDriver with ChromeDriver or GeckoDriver as, although it’s the heaviest and hardest to install, it’s the most stable and feature complete so your tests have the most realistic and reliable output.

A broad comparison of the three is:

Unit

Functional

Acceptance

Speed

Fastest

Fast

Slow

Scope

Classes/methods

App

End to End

Config file

unti.suite.yml

functional.suite.yml

acceptance.suite.yml

Continuous Integration

Codeception provides great support for working with CI services. Firstly, it prints test results to stdout which makes it immediately compatible with pretty much all CI services. Beyond this, it outputs all debug data to an output folder. This is really handy for CI as you can zip up this folder and save it as an artefact, so if your test fails, you can analyse the output and see what went wrong. In the case of acceptance tests, you can even save a screenshot of what happened when the tests failed. It also supports running acceptance tests on headless servers, using Selenium and ChromeDriver so you don’t need a special VM to run acceptance tests.

There is also good support for Docker, there is already a Codeception Docker image on Docker Hub, which provides basic functionality out of the box.

You can also instantiate a standalone selenium server using Docker.

Modules

Codeception provides a variety of modules straight out of the box. They roughly fall into one of three categories: framework drivers, external services and generic helpers.

The framework drivers allow Codeception to run functional test across different frameworks, not just Laravel. Currently supported frameworks are: Laravel 5, Lumen, ZF1, ZF2, Yii1, Yii2, Symfony, Silex and Zend Expressive. This is useful, as you can migrate tests between frameworks, and you can use the same tools across different frameworks and environments.

External service modules provide helpers and reduce boilerplate code when making assertions on other services. Examples of these drivers include: Facebook, Redis, Memcached, MongoDb, Apc, AMQP. The main purpose of these drivers is to reduce the amount of boilerplate you have to write when writing tests that interact with these services.

Generic modules provide modules that are broader in purpose. For example: Filesystem, FTP, Cli, Mock, and DataFactory. These all do very different things, but each provides access to test methods that most suites are likely to require at some point.

What is the main benefit of Codeception?

The Codeception module system is one of the main benefits of using Codeception over just PHPUnit and Dusk. It hugely reduces the amount of code you have to write as well as the amount of time you spend writing tests.

Although Laravel already has some excellent testing tools, Codeception is better established and provides a much more feature-complete testing experience. The documentation is very well-written and goes into lots of detail, there are loads of community supported drivers and modules, and the framework itself promotes simple, reusable tests. When writing tests for your next Laravel project, I would thoroughly recommend checking out Codeception!