Testing Your APIs with Bruno CLI in an Azure DevOps Pipeline

Published by Armel GANNE
Category : Azure / DevOps
04/06/2025

Automated testing plays a central role in ensuring the quality and reliability of software deliveries. However, automating API tests within CI/CD workflows remains a challenge for many teams. The CLI client Bruno offers an innovative solution to simplify this task. This open-source tool allows for fast, efficient, and integrated execution of API tests. In this article, we’ll explore how to configure a CI/CD pipeline in Azure DevOps to automate these tests.

 

Importance of Automated Testing and the Challenges of API Testing

 

Test automation helps prevent regressions and ensures that APIs function correctly. Here are the main challenges encountered when integrating these tests into a CI/CD workflow:

  • Environment management: Tests must run across multiple platforms.
  • Secret protection: API credentials and keys must not be exposed in the pipeline.
  • Performance: Tests should execute quickly without increasing pipeline duration.
  • Error management: Clear logs are needed to quickly identify failure causes.

 
 

Introducing Bruno and Bruno CLI

 

Bruno, an alternative to Postman, is an open-source tool designed specifically for testing REST APIs. It stands out for its performance and simplicity, providing a smooth and efficient experience. Its graphical interface includes all essential features of an HTTP client, ensuring intuitive usage. Additionally, its Tests tab makes it easy to define assertions, streamlining the request validation process.

What really sets it apart is the ability to store API requests as code within project files. This approach offers full control over API requests and tests, facilitating sharing, change tracking, and collaboration within technical teams—especially thanks to its seamless integration with Git.

For more information, see the official documentation.

 

Screenshot of Bruno

 
 

Why Use Bruno CLI?

 

Bruno is known for its ease of use, offering intuitive syntax and commands that integrate well via its CLI. It also brings flexibility by integrating seamlessly into PowerShell scripts and CI/CD pipelines, making it a great fit for development workflows.

That said, using Bruno may require some initial learning, especially due to the specific structure of its test collections, which may take time for new users to get familiar with.

 
 

Architecture of an Azure DevOps Pipeline with Bruno CLI

 

In Azure DevOps, we created a Bruno Test Task Group (BT) that enables API operations using the Bruno CLI. This Task Group’s parameters include essential information such as the API Key, the authentication scope, the Bruno project root path, and the test request folders.

 

Azure DevOps pipeline example

 

The Task Group is structured into four steps:

  1. Extracting secrets from Azure Key Vault
  2. Installing necessary tools (Node.js, Bruno CLI, etc.)
  3. Running tests with Bruno CLI
  4. Publishing test results in Azure DevOps

 
 

1. Extracting Secrets with Azure Key Vault and Azure CLI

 

The first step is to retrieve secrets stored in Azure Key Vault, such as clientID and clientSecret, which are required to authenticate API calls. We use Azure CLI for this, allowing secrets to be accessed directly from the command line:

 

$Secret = az keyvault secret show --name "secret-key-name" --vault-name "key-vault-service-name" --query "value" -o tsv

 

Once the secret is retrieved, it’s stored in an Azure DevOps pipeline variable to be reused in the next steps:

 

Write-Host "##vso[task.setvariable variable=Secret]$Secret"

 

To ensure secure access, it’s best to grant permissions to the Service Principal associated with Azure DevOps on the Key Vault rather than exposing a Personal Access Token (PAT) in the pipeline. This maintains high security while avoiding permission issues.

 
 

2. Installing the Tools

 

Before running tests, Node.js and Bruno CLI must be installed on the runner agent using:

 

npm install -g @usebruno/cli

 
 

3. Configuring and Running Tests with Bruno CLI

 

Setting Paths and Configuration

Tests are executed from the Bruno project root ($path) by specifying the folder containing the operations to be tested ($folder). For correct Azure DevOps pipeline execution, manage the environment configuration file ($configFilePath) and make sure test reports are correctly generated in ($destinationReport).
Name the environment file based on the current pipeline environment ($(Env_Maj)), stored in an Azure DevOps variable library.
This approach supports multiple deployment environments.

 

$folder = "$(QueriesFolder)"
$path = "$(ProjectPath)"
$configFilePath = "$path\environments\$(Env_Maj).bru"
$destinationReport = "$path\Results\report.xml"

 

Dynamically Updating the <ENV>.bru Configuration File

Before executing API tests, it is essential to inject the required variables dynamically into the <ENV>.bru configuration file. The PowerShell script below handles this by reading the existing file, modifying or adding the vars section (API host, credentials, client ID, etc.), and saving the updated file.

This ensures test consistency by aligning variable values with those defined in the pipeline.
If the config file doesn’t exist, the script generates a base one to prevent errors.

Error handling is included to guarantee process reliability by validating file access and permissions. This method allows fully automated and environment-specific configuration updates, eliminating manual intervention.

 

# Read, modify, and save the configuration file
Write-Host "Updating configuration file: $configFilePath"

if (Test-Path $configFilePath) {
    $existingContent = Get-Content -Path $configFilePath -Raw
} else {
    $existingContent = ''
    Write-Warning "Configuration file '$configFilePath' not found. A new file will be created."
}

$newVarsSection = @"
vars {
    apimHost: $(Common_Host_Name)
    scope: $(Scope)
    apiKeyExternal: $subscriptionValue
    client_id: $clientIdValue
    client_secret: $clientSecretValue
}
"@

$updatedContent = $existingContent -replace '(?s)vars:secret\s*\[.*?\]', ''

if ($existingContent -match '(?s)vars\s*{.*?}') {
    $updatedContent = $updatedContent -replace '(?s)vars\s*{.*?}', $newVarsSection
    Write-Host "Existing 'vars {}' section replaced."
} else {
    $updatedContent = $updatedContent + "`r`n" + $newVarsSection
    Write-Host "New 'vars {}' section added."
}

try {
    Set-Content -Path $configFilePath -Value $updatedContent -Force
    Write-Host "Configuration file successfully saved: $configFilePath"
} catch {
    Write-Error "Failed to update configuration file. Check permissions and path."
    exit 1
}

 

Running Tests with Bruno CLI

Once the configuration file is updated, run the tests using the following command. The --output and --format options are used to generate a JUnit-style test report:

 

bru run --env "$(Env_Maj)" --output "$destinationReport" --format junit $folder

 
 

4. Publishing Test Results in Azure DevOps

 

The XML files containing test results are published using Publish Test Results from the path $destinationReport.
By integrating this step into the pipeline, the team ensures that test reports are correctly processed and available.
Once published, results appear under the Tests tab in Azure DevOps, showing execution history and details for any failed tests.

 

Result Panel in Azure DevOps

 
 

Conclusion

 

Integrating Bruno into an Azure DevOps pipeline brings real benefits to API testing.
By automating tests, teams save time on manual validations while ensuring greater deployment reliability.
Embedding requests directly into code simplifies versioning and fits naturally into developer workflows.

To ensure a secure and efficient pipeline, several best practices are critical:

  • Use Azure Key Vault to secure secrets and manage access precisely.
  • Validate configuration files before execution by testing assertions and report generation locally.
  • Ensure the runtime environment is correctly configured (Bruno requires Node.js).
  • Handle errors gracefully to avoid breaking the pipeline due to minor issues.

In practice, this approach saves time by reducing repetitive tasks and minimizing human error.
Automated tests ensure greater reliability across all deployment environments.
Some improvements are still possible, such as improving test data set management to ensure stable and reproducible tests.
Likewise, customizing reports with specific metadata could further enhance result analysis.

Adopting Bruno in a CI/CD pipeline is a step toward robust, high-performance automation that improves API test quality.