# CI/CD integration

Azure DevOps and GitHub Actions pipelines that run your service tests on every push.

Once you have tests for your Zato services, you want them to run automatically on every commit. This page shows how to set up Azure DevOps and GitHub Actions to run your tests and report results.

**Note:** If you're new to unit testing with Zato, check the [tutorial](https://zato.io/docs/api-testing/tutorial.html) first. The [GitOps blueprint](https://zato.io/tutorials/devops/deployment.html) includes ready-to-use pipeline configurations.

The best way for you get started with it is simply to clone the [blueprint repository](https://github.com/zatosource/zato-project-blueprint), copy it over to your own organization on Azure or GitHub, and just push it to git.

This will automatically trigger the tests, and you'll get a report as below.

## Azure DevOps {#azure-devops}

## GitHub Actions {#github-actions}

## How the pipelines work {#how-the-pipelines-work}

Both pipelines ([Azure](https://github.com/zatosource/zato-project-blueprint/blob/main/azure-pipelines.yml) and [GitHub](https://github.com/zatosource/zato-project-blueprint/blob/main/.github/workflows/test.yml)) run tests automatically when you push to `main` or `develop`. You can customize them per your own requirements, the default configuration is meant to be a starting point that you adjust as needed.

The key part of each configuration is the test command:

```text
PYTHONPATH=myproject/impl/src pytest myproject/testing/tests/ -v --junitxml=test-results.xml
```

This sets `PYTHONPATH` so Python can import your services from `impl/src`, runs pytest against your test directory, and outputs results in JUnit XML format. The pipelines then publish these results to the CI/CD platform's UI so you can see pass/fail status directly.

## Project structure {#project-structure}

In your blueprint-based projects, place your tests under `testing/tests`, because the pipelines look for them in that directory, so your entire project will look like this:

```text
├── .github
│   └── workflows
│       └── test.yml
├── azure-pipelines.yml
└── myproject
    ├── impl
    │   └── src
    │       └── api
    │           └── crm.py
    └── testing
        └── tests
            └── test_crm.py
```

In short:

| Path | Description |
| --- | --- |
| [azure-pipelines.yml](https://github.com/zatosource/zato-project-blueprint/blob/main/azure-pipelines.yml) | Azure DevOps pipeline |
| [.github/workflows/test.yml](https://github.com/zatosource/zato-project-blueprint/blob/main/.github/workflows/test.yml) | GitHub Actions workflow |
| [myproject/impl/src/api](https://github.com/zatosource/zato-project-blueprint/tree/main/myproject/impl/src/api) | Your services |
| [myproject/testing/tests](https://github.com/zatosource/zato-project-blueprint/tree/main/myproject/testing/tests) | Unit tests for the services |

Note that both Azure DevOps and GitHub require for their configuration files to be in these specific locations, otherwise the pipelines won't be triggered.

## Reference {#reference}

For reference, to make it easier for [your AI](https://zato.io/docs/ai/connect-your-copilot.html) to set it all up, here's what the pipeline files look like by default:

### Azure DevOps {#azure-devops_1}

```yaml
trigger:
  branches:
    include:
      - main
      - develop

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: UsePythonVersion@0
    inputs:
      versionSpec: '3.12'
    displayName: 'Set up Python'

  - script: pip install pytest zato-testing
    displayName: 'Install dependencies'

  - script: |
      PYTHONPATH=myproject/impl/src pytest myproject/testing/tests/ -v --junitxml=test-results.xml
    displayName: 'Run tests'

  - task: PublishTestResults@2
    inputs:
      testResultsFormat: 'JUnit'
      testResultsFiles: 'test-results.xml'
    condition: always()
    displayName: 'Publish test results'
```

### GitHub Actions {#github-actions_1}

```yaml
name: Test Zato services

on:
  push:
    branches: [main, develop]

permissions:
  contents: read
  checks: write

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
  test:
    name: Test services
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: pip install pytest zato-testing

      - name: Run tests
        run: PYTHONPATH=myproject/impl/src pytest myproject/testing/tests/ -v --junitxml=test-results.xml

      - name: Publish test results
        uses: dorny/test-reporter@v1
        if: always()
        with:
          name: Test results
          path: test-results.xml
          reporter: java-junit
```

## Other pipelines {#other-pipelines}

The same approach works with any CI/CD platform - [GitLab CI](https://docs.gitlab.com/ci/), [Jenkins](https://www.jenkins.io/), [Bitbucket Pipelines](https://bitbucket.org/product/features/pipelines), [CircleCI](https://circleci.com/), or any other system that can run shell commands.

The core concept is always the same:

1. Set `PYTHONPATH` to point to your services directory so Python can import them
2. Run pytest with `--junitxml` to produce a standard test report
3. Use your platform's built-in test result publisher to display the JUnit XML

Every CI/CD platform has its own way to publish JUnit results - consult your platform's documentation for the specific syntax.

## Learn more {#learn-more}

- [API testing](https://zato.io/docs/api-testing/) - Tests in plain English, run against real or mocked connections
- [API testing tutorial](https://zato.io/docs/api-testing/tutorial.html) - A complete test suite, from the first scenario to the report
- [Testing REST connections](https://zato.io/docs/api-testing/rest.html) - Mock outgoing REST calls and assert on what your service sent
- [Testing SQL connections](https://zato.io/docs/api-testing/sql.html) - Mock database queries and the rows they return
- [Testing LDAP connections](https://zato.io/docs/api-testing/ldap.html) - Mock directory lookups and the entries they find
- [Testing Microsoft 365](https://zato.io/docs/api-testing/ms365/) - Mock mailboxes, calendars, SharePoint, Teams and the rest
- [Testing service invocations](https://zato.io/docs/api-testing/services.html) - Services that call other services, with the callees mocked
- [Testing Jira connections](https://zato.io/docs/api-testing/jira.html) - Mock Jira API calls and the issues they return
