Implementing Automated Testing with Codeception
We're excited to share our experience implementing automated testing into legacy websites, where it has helped improve functionality and user experience.
This article serves as a follow-up to part one, where we discussed essential tools and best practices for QA in Drupal.
Dating back to 2018, our collaboration with George Washington's Mount Vernon, the renowned historic estate in the United States. Together, our goal has been to create a seamless user experience that complements the excitement of a visit to this iconic landmark. To learn more about our journey, don't miss our case study on George Washington's Mount Vernon.
Reasons for Choosing Autotests in Legacy Website
1. Extensive functionality
In the early stages of website maintenance, we often encountered situations where, after updates or the introduction of new features, problems would manifest themselves in obscure corners of the site. In many cases, even the client could not recall the intended purpose or expected behavior of these features. Given the size of the site, there were occasions when we received requests to fix issues in certain areas of the site that had been developed by previous teams without our knowledge.
2. Integration with third-party services (YouTube, Tessitura, TNEW)
The project involves a significant degree of integration with various services, with Tessitura playing a prominent role. Tessitura is a Customer Relationship Management (CRM) and ticketing software that regularly updates and occasionally modifies its Application Programming Interface (API), which sometimes results in a break in backward compatibility.
3. Ticketing platform
The ticketing platform contains a variety of complex logic and use cases. Manual testing of this complexity would prove tedious and time-consuming, especially given the platform's constant evolution as new features and logic are introduced. Manually reviewing each case after each deployment is a demanding undertaking. We concluded that the use of automated testing was essential in this context.
Why Codeception
Codeception is a PHP testing framework that stands out from the crowd. It offers many advantages. Primarily, its seamless integration with PHP ensures a better understanding of the code, giving developers an edge when creating efficient testing. Codeception also makes the testing process easy and accessible for newcomers.
Codeception covers all testing needs: Functional, Acceptance, and Unit testing under one unified interface, saving valuable time and effort. Codeception also comes with comprehensive and transparent documentation. This makes implementation and debugging much easier. By engaging the broader community, Codeception benefits from strong support and continuous improvement through collective knowledge exchange.
For developers looking to harness the power of PHP testing, Codeception proves to be an ideal choice, offering a comprehensive solution with the utmost simplicity and effectiveness.
Codeception API Tests
Codeception enables the execution of various requests with authentication, along with the flexibility to include custom headers. It facilitates the validation of response structures, ensuring that all expected fields are present with the correct data types. It also allows for data validation within the response.
Validate Response Structure (JSON/XML)
$I->sendGet('/users/1');
$I->seeResponseCodeIs(HttpCode::OK);
$I->seeResponseIsJson();
$I->seeResponseMatchesJsonType([
'id' => 'integer',
'name' => 'string',
'email' => 'string:email',
'homepage' => 'string:url|null',
'created_at' => 'string:date',
'is_active' => 'boolean'
]);
Expected response fields are defined by specifying their data types. For example, an ID field should be of type integer, while the name field should be of type string. These validation rules are the simplest form. When a response is received, Codeception performs a comprehensive check to ensure that all expected fields are present and that their data types match the request.
Validate Response Data (JSON/XML)
// matches {"result":"ok"}'
$I->seeResponseContainsJson(['result' => 'ok']);
// it can match tree-like structures as well
$I->seeResponseContainsJson([
'user' => [
'name' => 'davert',
'email' => 'davert@codeception.com',
'status' => 'inactive'
]
]);
This illustrates the validation of specific values within the response. Once a request is sent and a JSON response is received, the validation extends beyond structure matching to include specific data validation. For example, this may include confirming the presence of an expected username or email.
Codeception Acceptance Testing
Acceptance testing allows reproducing tester actions within specific scenarios. These tests can then be automated to observe the results. Codeception supports two execution modes for acceptance testing:
- PhpBrowser
This approach emulates browser requests using PHP without relying on an actual browser. This method has certain limitations, such as the lack of JavaScript support. Consequently, the tests assume that if an element is present in the response (i.e., within the HTML source), it is visible to the user. In reality, however, it may remain hidden due to overlapping elements. Despite these limitations, these tests are exceptionally fast and do not require any additional infrastructure. Note that there are some limitations; for example, only links with valid URLs or submit buttons within forms can be clicked. It is not possible to interact with anchors or links that call JavaScript. Also, the field population is limited to fields included in a form.
- WebDriver
This method executes tests in a real browser environment, such as Chrome or Firefox. It allows the use of real browsers for test execution, such as Firefox. This approach allows JavaScript code to be executed on web pages, allowing for more precise UI testing. However, there are some drawbacks. WebDriver requires additional infrastructure, either Selenium or a headless browser. In addition, the execution speed of these tests is comparatively slower than that of tests executed through PhpBrowser, due to the complete web page loading process.
Login Codeception Acceptance Tests
Login Scenario:
$I->amOnPage('/login');
$I->fillField('username','davert');
$I->fillField('password','qwerty');
$I->click('LOGIN');
$I->see('Welcome, Davert!');
The process of writing acceptance tests in Codeception is the same regardless of whether you use PhpBrowser or WebDriver. There are only a few nuances between the two approaches. Switching between the PhpBrowser and the WebDriver is a very straightforward task.
The tests are written in first-person narrative, where the actor describes being on the login page, filling in the username and password fields, clicking the login button, and expecting to see a welcome message confirming a successful login.
Coverage of Acceptance Tests in Legacy Website
Acceptance Tests for the Video Portal
We have devised simplistic tests to ascertain the functionality of all web pages. These tests embody simplicity as the video portal does not encompass intricate logic. Consequently, the tests primarily verify the existence of elements on the page and the smooth functioning of the pages themselves, including the absence of any 500 errors and the proper display of essential sections. This approach bears semblance to Diffy, albeit operating on builds and offering a less granular UI comparison.
Acceptance tests for the Ticketing Platform
Encompass a substantial amount of logic, rendering the process of test composition considerably more intricate. A sample of a ticketing acceptance test is provided below to exemplify its structure:
public function testDayOfVisitSelection(AcceptanceTester $I, TicketingPage $ticketingPage) {
$I->amOnPage($ticketingPage->url);
$I->wantTo(text:'Skip member popup.');
$ticketingPage->dateOfVisitStep->closePopup();
$I->wantTo(text:'Choose a date from the current month and go to the next step.');
$today = new \DateTime();
$ticketingPage->dateOfVisitStep->chooseDate($today);
$I->click(link:'Continue');
$I->waitForText($ticketingPage->groundsPassStep->arriveMessage);
$selected_date_text = $I->grabTextFrom($ticketingPage->dateOfVisitStep->userSelectedDate);
assertEquals($today->format(format:'F j, Y'), $selected_date_text);
A user is currently on the ticketing page, where they are initially presented with a pop-up inquiring about their membership status. The first step of the test involves closing this pop-up and proceeding to select the current date from the calendar. Upon clicking "Continue," the user is directed to the next step, where we verify the proper display of the required message. Subsequently, we confirm that the selected date is correctly shown in the following step.
Tests can exhibit even greater complexity, such as connecting to databases, retrieving specific data, and utilizing it within the test scenarios. Parallel sessions can also be employed, where one user, for instance, an administrator, makes changes in the administration panel while another user, not logged in, verifies frontend elements. The range of possibilities is extensive.
Implementing Autotests: An Overview of the Pipeline
In terms of implementation, all these tests have been integrated into our build process, ensuring their execution with each build. The overall pipeline follows the subsequent sequence:
- Composer Audit. This step scans the installed packages for reported security vulnerabilities, and if any packages with known security vulnerabilities are found, it exits with an error code.
- Composer Validate. This step checks the validity of the composer.json file, ensuring it adheres to the required structure and format.
- Code Sniffer. Here, the PHP_CodeSniffer tool is executed to ensure that all PHP and Drupal coding standards are met, verifying code style, formatting, and adherence to best practices.
- PHP Mess Detector. This step analyzes the code quality and complexity using the PHP Mess Detector tool, identifying potential issues such as code duplication, overly complex code, and possible bugs.
- PHPStan. PHPStan analysis is performed in this step, which involves static analysis of the codebase to detect type-related errors, and potential bugs, and improve overall code correctness.
- TwigCS. The TwigCS tool is employed to ensure that all TWIG coding standards are met, validating the syntax, formatting, and conventions of TWIG templates using this guideline.
- Kernel Tests. This step involves the execution of Drupal kernel tests, which test the functionality and integration of the Drupal core and its modules at a low level.
- Unit Tests. Drupal Unit tests are executed in this step, focusing on testing individual components, functions, and classes to ensure their proper functionality in isolation.
- Codeception. The Codeception testing framework is utilized to execute acceptance tests on the build validating the behavior and functionality of the application as a whole.
After installing the necessary dependencies and static code analysis, we initiate unit and integration testing, focusing on Drupal-related tests for efficient execution. If any of these tests fail, then there is no point in continuing with the build process. Next, we generate the build and run conceptual tests on the finished build. These tests cover the required functionality and a pre-existing database. The execution of API tests and acceptance tests follows this. If any of these tests fail, the build is considered unsuccessful.
Conclusion
In the ever-evolving landscape of web development, mastering the art of automated testing has proven to be an indispensable asset. Especially when dealing with legacy websites. The decision to use Codeception, the versatile PHP testing framework, with its unified interface, built-in browser emulator, and seamless integration with Drupal, was a natural one.
As testing evolves, one thing remains certain: embracing the art of automated testing opens a realm of possibilities, empowering developers to take websites to new heights and ensuring a seamless digital experience for users worldwide.
LET'S CONNECT
Get a stunning website, integrate with your tools,
measure, optimize and focus on success!