June 9, 2023
9
min read

Continuous Testing Best Practices

Continuous testing is essential for modern software development. Learn eight best practices to maximize success in our free guide.

Continuous testing is a software development practice in which quality assurance efforts are integrated throughout the development cycle. While more traditional testing models involve sending completed code to be independently (and often manually) tested, continuous testing utilizes automated tests to provide developers feedback each time code is submitted to a repository. Its primary goals are to improve software quality and speed up development by catching bugs early, which can shorten feedback loops and reduce bottlenecks.

With the rise of agile methodology and automation within CI/CD pipelines, continuous testing has become an integral part of the software development cycle for many organizations. This article presents eight best practices to consider adopting in your continuous testing strategy. Whether you are new to continuous testing or looking for ways to improve your current approach, we hope these practices will help your organization leverage the many benefits of continuous testing.

Summary of continuous testing best practices

Continuous testing best practice Description
Organizationally align Shift testing efforts left , and promote a quality-driven engineering culture across the entire software development team
Choose the right tooling Select one or more test automation tools based on project requirements, and consider EaaS solutions for QA environments
Integrate with the CI/CD pipeline Automate functional, regression, integration, performance, and security tests to run as prerequisites for merging code into a trunk branch
Use an environment as a service (EaaS) provider Ephemeral environments facilitate continuous testing by providing on-demand and short-lived preview environments in which to run automated and manual tests
Prepare your code for EaaS implementation Adopt EaaS best practices, such as containerizing your code, using a Git-based version control system, and seeding test databases
Address security as a first-class citizen Integrate security into CI/CD practices and include automated security testing in your DevOps strategy
Measure and refine your process Identify and track relevant testing metrics according to business goals to optimize your continuous testing approach
Remember documentation Consistently create, update, and verify documentation to ensure that all team members have access to up-to-date information on test design, coverage, execution, metrics, and results

Explanations of continuous testing best practices

The following sections elaborate on the best practices summarized in the table above. They also provide actionable steps to help your organization adopt an effective and efficient continuous testing strategy.

Organizationally align

Successfully adopting continuous testing requires an engineering culture devoted to quality assurance. In an ideal scenario, this quality-focused culture would be company-wide and shared among developers, team leads, product managers, and even stakeholders. However, development teams in any organization can benefit from continuous testing with or without support from upper management.

Developers and engineering team leads contribute to this culture in many ways, such as improving test coverage on legacy code, clearly defining and adhering to code quality standards, and embracing quality-driven development practices like test-driven development, pair programming, and code reviews. The development team may also need to advocate for and explain the importance of continuous testing to stakeholders, who may need to adjust budget and timeline expectations accordingly.

In many cases, placing a greater emphasis on QA involves both technical and cultural shifts in how an organization develops software. This can mean onboarding new hires differently and learning new workflows and testing tools.

As with any significant change to team culture and development practices, communication and allowing time to transition are key. Implement new practices gradually, and leave time for feedback and questions on new procedures. Educate all team members on the long-term benefits of automated testing, CI/CD, and other QA efforts. Finally, promote the understanding that software quality is a shared responsibility and requires communication and collaboration among different teams.

Choose the right tooling

With a wide variety of open-source and commercial testing tools available, choosing the correct tools for your team can be daunting. Here are some key factors to consider:

  • Compatibility with existing tools: Will the solution integrate easily into your existing CI/CD pipeline?
  • Ease of setup and test data management: How much overhead will be required to get the testing tool running with the necessary test data?
  • Code vs. no-code: What level of technical knowledge do the team members configuring and maintaining the testing tool possess?
  • Programming language: Are tests written in the same language as the production code? If developers must switch between languages or IDEs to use a testing tool, this will be a barrier to adoption.

When choosing a testing tool, it is important to consider both your team’s current and future needs, as migrating between tools can take significant developer time. Compatibility with the existing infrastructure, ease of setup, and programming language considerations are vital aspects of the upfront cost of implementing a testing solution. On the other hand, choosing between a code-based and no-code solution can have great implications for which team members will be responsible for testing in the future.

Your team will likely require multiple tools for unit, integration, end-to-end, and UI testing. We recommend focusing on unit testing first because unit tests are the simplest to implement and least expensive to run. If possible, choose a unit testing tool that allows developers to write tests in the same language as that used for production code, such as Mocha for JavaScript, NUnit for .NET languages, or JMockit for Java.

For more sophisticated end-to-end and UI testing, we have included some code and no-code options below.

Code-based:

  • Playwright
  • Selenium
  • Appium
  • Travis CI
  • Unified Functional Tester

No-code:

  • Katalon Studio
  • testRigor
  • TestCraft
  • Selenium IDE
  • Ranorex Studio

In addition to a testing tool, your team will require one or more QA environments in which to run tests. We recommend using environment as a service (EaaS) capabilities to run tests within ephemeral preview environments—more on this in later sections.

Environments as a Service (EaaS)
Platform
Qovery
Release Hub
Uffizzi
Lightweight and Fast (All-Container Solution)
Easy Setup
(Based on Docker Compose)
Reusable Github Actions Workflow

Cost
$$$
$$$$
$
See Comparison Table
Platform
Lightweight and Fast (All-Container Solution)
Easy Setup
(Based on Docker Compose)
Reusable Github Actions Workflow
Cost
Qovery
$$$
Release Hub
$$$$
Uffizzi
$
See Comparison Table

Integrate with the CI/CD pipeline

Continuous integration and continuous delivery (CI/CD) rely heavily on continuous testing. Rather than postponing QA efforts to later stages of a software release, continuous testing within a CI/CD pipeline seeks to identify and fix bugs as close to their introduction into a codebase as possible. This results in a more efficient development process.

Implementing continuous testing requires test automation. Some of the most common types of tests to automate include:

  • Unit tests
  • Functional tests
  • Regression tests
  • Integration tests
  • Performance tests
  • Security tests

Automated tests are run on an individual build as a quality gate before a piece of code can pass to the next stage of the CI/CD pipeline. This can be done locally (often within a container runtime), in a cloud-based environment, or both. An example of a CI/CD release pipeline is shown below.

Example of a CI/CD release pipeline (adapted from source)

Use an environment as a service (EaaS) provider

Continuous testing requires a QA environment to conduct automated and manual testing. Persistent QA environments meet this requirement by mimicking production environments as closely as possible. However, this means deploying multiple versions of the same application on identical and often costly IT infrastructures, which incurs significant deployment and maintenance costs. In addition, using persistent QA environments often leads to bottlenecks when multiple changes to a codebase arrive concurrently.

Using EaaS for testing alleviates these issues by providing ephemeral QA environments that are easily spun up and taken down on demand. The goal is to cut costs and simplify QA workflows by automating the creation of ephemeral QA environments that provide the infrastructure needed to meet the testing requirements for a given feature branch.

EaaS providers help solve these problems by providing the IT infrastructure and software to run an application in an isolated environment. EaaS providers like Uffizzi offer ephemeral preview environments, which can be used to test a particular feature branch in isolation before merging it into a trunk branch. Preview environments can be automatically spun up when a pull request is opened and taken down once tests have passed and the pull request is merged or closed. There is no limit to the number of preview environments that can coexist, so each developer’s code can be tested independently and simultaneously, if necessary. This alleviates or eliminates many financial and workflow difficulties associated with persistent test environments.

See how Spotify packs more into every release with ephemeral environments
Read Case Study

Prepare your code for EaaS implementation

To help integrate EaaS into your development and deployment processes, we have included a few best practices below. For a more comprehensive list, see this article on ephemeral environment best practices.

Containerize your code

An effective EaaS platform should include infrastructure as code (IaC), meaning that it should be able to provision environments based on an IaC definition such as a Docker Compose file. Once the IaC definition is provided, the EaaS platform can deploy ephemeral versions of your application, containing all of the services, libraries, configuration files, and binaries needed to run the application in a production-like environment.

Minimize data requirements

If your application relies on a stateful database, you will likely need to run test cases with representative data. However, deploying more complex and data-heavy test environments is a costly operation even when using an EaaS provider. It is worth assessing whether your test plan’s data requirements are mission critical; if not, it may be worth scaling back or even eliminating data requirements for a given test environment.

Seed test databases

Once you have identified testing goals that require sample data, you will need a way to load persistent test data into ephemeral databases spun up on demand by the EaaS provider. There are a variety of ways to achieve this, including using an ORM with built-in database seeding or loading a dump file. For example, if you are using a SQL database, a SQL dump file stored in an object storage service like Amazon S3 or Google Cloud Storage can be loaded upon container initialization. For more information on this and other seeding methods, see this guide.

Address security as a first-class citizen

As we have seen, continuous testing within a CI/CD pipeline requires a substantial amount of automation. Besides automating test cases, it has become increasingly common to automate build and delivery processes, auto-scaling, configuration management, and other tasks in cloud-based environments. This introduces challenging security concerns best addressed by integrating security efforts throughout development and operations processes. We will examine two ways to address security challenges below.

Continuous security testing

Continuous security testing (CST) is a security practice that has become essential within CI/CD pipelines. As a form of continuous testing, CST shifts security concerns left to prevent vulnerabilities being introduced into an environment that attackers could target. Continuous security tests monitor your code and application dependencies for security issues and the exposure of sensitive data throughout development and deployment processes. In addition, CST services can be configured to respond automatically to security incidents by removing malware, deactivating services, or installing patches or upgrades to an affected system. Some popular CST tools include Intruder, CrowdStrike, and Snyk.

Dynamic application security testing (DAST)

In contrast to CST’s emphasis on shifting security concerns left, DAST simulates automated attacks on a web application once it is deployed. DAST is a form of black-box security testing, meaning that DAST tools simulate attacks from the outside in and do not require access to the source code. The goals of DAST are to mimic real cyberattacks and find vulnerabilities before they are exploited.

The primary disadvantage of DAST is that, in shifting security concerns right, vulnerabilities are identified late in the development process and can be costly to fix. However, DAST can be extremely helpful in finding runtime and environment-related issues that are not possible to identify before an application is deployed. For this reason, combining DAST with other security practices is highly beneficial.

DevSecOps platforms like GitLab provide DAST analyzers that scan web applications and APIs for vulnerabilities and provide reports on results. If your application is containerized using Docker, DAST can be configured relatively easily within the same YAML file used for the build, test, and deploy stages.

 stages:
  - build
  - dast

include:
  - template: DAST.gitlab-ci.yaml

deploy:
# standard deployment configuration here
...

dast:
  services:
    - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      alias: myapp

variables:
  DAST_WEBSITE: https://myapp
  DAST_FULL_SCAN_ENABLED: "true"
  DAST_BROWSER_SCAN: "true"

A sample GitLab CI YAML file configuring DAST to scan a web application

Measure and refine your process

Including continuous testing in a CI/CD pipeline does not, in itself, guarantee software quality or a more efficient development process. To leverage these benefits, your continuous testing strategy requires thoughtful implementation and consistent reassessment to ensure that it provides useful information to the right people and helps your product meet your business goals.

If your team is getting started with continuous testing, begin by creating a small number of test cases and add coverage gradually where necessary. It is also recommended to adhere to the principles of the test pyramid, meaning that a larger number of relatively simple and cheap tests is preferable to a smaller number of more complex and expensive tests. For example, running several unit tests is often more efficient than running one integration test, and running a few integration tests is often more efficient than running one end-to-end test.

The testing pyramid (source)

In addition, your organization may benefit from keeping the following tips in mind:

  • Do not automate too many tests.
  • Ensure that development teams have access to sufficient testing environments.
  • Avoid testing environments that are too costly to maintain.
  • Be wary of false positives and false negatives in test results.

Addressing these issues in your test plan will help your team avoid writing large, unmanageable test suites that create bottlenecks and slow down development and delivery processes.

Finally, to help your team better understand and reevaluate the effectiveness of your current continuous testing strategy, consider tracking the following metrics:

  • Total test duration: The amount of time it takes to run a test suite
  • Code coverage: The percentage of code covered by test cases
  • Test pass percentage: The ratio of the number of passing test cases to the total number of test cases
  • Code quality: Can include quantifiable metrics such as the number of duplicated lines of code, code vulnerabilities, lines of code per function or class, etc.
  • Lead time to deploy: The amount of time between code being committed and running successfully in production

There is no one-size-fits-all approach to tracking metrics, so be sure to choose the right metrics for your product according to your business goals.

Remember documentation

Documentation on test design, coverage, execution, metrics, and results contributes to an effective continuous testing strategy. It removes ambiguity about testing activities, helps onboard new hires, and allows your team to improve your existing testing strategy by examining what is already in place.

However, creating high-quality documentation is time-consuming, so ensure that your team creates only those documents that contribute to product success. To help your team write documentation efficiently, consider the following tips:

  • Emphasize code quality and readability throughout your development process to minimize the amount of documentation your team must create.
  • Start with a small amount of documentation and expand if needed.
  • Ensure that all parts of your documentation are in sync with the codebase, and remove any sections that are no longer relevant.
  • Save time by using a continuous documentation tool like Swimm.

Every organization’s needs for test documentation will differ. As with other aspects of the continuous testing process, it is essential to regularly refine your approach to creating and managing test documentation.

Conclusion

Continuous testing is both a development process and a mindset. While it can produce better development efficiency and product quality, continuous testing can be challenging to adopt and maintain effectively. The best practices above will help your organization avoid common pitfalls and successfully integrate continuous testing into current and future software projects.

Uffizzi logo
Environments as a Service
Learn More
preview icon
Empower your devs with an environment for every pull request
time icon
Avoid release delays that cost millions in missed revenue
Velocity icon
Improve development velocity at scale by up to 50%
Lifecycle icon
Plan capacity in real time with lifecycle management built in
Learn More