At Elastic Path, having a quality mindset is the key to providing our customers with the best commerce experience. Elastic Path Commerce Cloud is a composable, microservices-based commerce solution which offers simple, flexible, and powerful APIs.
Microservice architecture means Elastic Path Commerce Cloud has a set of loosely coupled, collaborating services. Each service has its own responsibility. However, a set of services need to collaborate to fulfill the needs in most user journeys. For example, a simple user journey where a shopper wants to checkout will require the authentication service for identifying the shopper, catalog service to serve the products plus the cart, orders, inventory, and payment services for checkout.
To ensure this journey is working consistently every day after every release, we need thorough testing strategy in place. The following will highlight how testing is done in our software development lifecycle.
How do we ensure quality in Elastic Path Commerce Cloud?
The engineering team at Elastic Path has defined a test pyramid, inspired by the work of Martin Fowler and Chris Richardson, that follows industry best practices. The pyramid represents the complexity and speed of test execution.
From bottom to top, tests at the bottom layer (e.g., Unit Tests) are more isolated (less complex) and faster (speed). The tests at the top (e.g., end-to-end tests) are more complicated in terms of setup and slower because they require a fully deployed environment and various configurations of the software.
The diagram on the right describes the service and its relationship to the platform. A service is within a blue box whereas the purple boundary is a group of services integrated together (a deployed functional environment).
What are the different types of tests we run to ensure quality?
Unit tests are the lowest level of the tests. They are the most fundamental layer, focused on a class and functional methods. Unit tests provide the fastest feedback and have minimal dependencies on other parts of the service because they do not require a deployed service.
The tests are mostly written in the same language as the production code. The goal is to ensure the business logic within a class and method is tested both positively and negatively.
Component Tests test a service in isolation from other services. The goal is to ensure the service responsibilities are as expected.
The tests are on API level. For example, a POST request to add items to cart is tested by providing the required headers and requested body. Then the success response is validated to ensure the returned status code and body has the right information. We use Cucumber for the test cases and the backing code is in Typescript or Go, depending on the service.
The test cases cover all the business requirements that a service is responsible for, including data persistence and event processing. In addition, integration tests with database and messaging are also implemented here. There are test cases to ensure data persistence between the business logic and database. There are also test cases to ensure events are emitted for any API requests that need to emit certain events for consumers to consume.
This level of test requires the service and the dependencies (database, message broker) to be running.
These tests ensure that service to service communication works as services, often owned by different teams, evolve independently. When a service (consumer) talks to the other service (provider), there is a Contract between them. The consumer expects the provider to provide an agreed response. For example, if a specific field in the provider response (e.g., customer_name) is changed (e.g., to shopper_name), this will break the consumer as it expects customer_name.
In this case, it is a breaking change and can impact our customers. We adopt Pactflow as our contract testing broker. The contracts are stored in the Pactflow server. Both provider and consumer services will download the contracts and verify if the contract is still valid during the test run. This gives us confidence in communication between the paired services.
The goal of these tests is to ensure that services are configured and work correctly in a deployed environment.
Our end-to-end tests are written from an end user's perspective. We follow BDD and write tests in Cucumber. Each test scenario describes an end-user's journey when using the public APIs. We have two distinct types of user profiles - a seller or store admin, and the buyer or shopper. Each user profile has different journeys. For example, a seller has journeys like catalog management, promotions management, and orders management. While the shopper has journeys like browsing, registration, and checkout.
The tests are executed every time there is a new deployment from any service code changes. This is to ensure the commerce experience is still performing as expected. Any failures stop further release process. In addition, we have scheduled jobs that run the tests against the production environments to ensure the production has no outage or unexpected behaviors.
We have Commerce Manager which is a web-based user interface that allows business users to manage their stores.
The UI tests ensure that each page and their components are functioning as expected. Store admin journeys such as creating products, managing catalog, price books, promotions, orders, and other store settings need to be tested. The test scenarios are written in Cucumber format for readability and the implementation is SeleniumJS. The test uses Chrome driver to simulate the browser and selenium commands to execute the browser interactions (e.g., clicking on buttons).
UI testing is usually the slowest of the tests. We utilize the parallel testing feature which allows us to run multiple tests at one time. This significantly cut down our test cycles.
To achieve parallel testing, tests are designed to be isolated from each other. This allows us to run any single test at any time without dependencies.
At Elastic Path, our entire team is committed to quality. The Test Pyramid outlined above is the guideline that our engineering team follows to ensure our customers have a reliable platform to run their businesses. All tests are run in a continuous delivery pipeline. This gives us confidence in the quality of changes being released to production for Elastic Path Commerce Cloud. Stay tuned for future blog posts where we will talk about the non-functional testing we do for performance and security.